Skip to content

Commit 52b7df0

Browse files
Elena Rodionovaignatvilesov
Elena Rodionova
authored andcommitted
Labels issue (#28)
1 parent f3d256e commit 52b7df0

File tree

6 files changed

+187
-63
lines changed

6 files changed

+187
-63
lines changed

src/behavior.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -28,12 +28,12 @@
2828
import Selection = d3.Selection;
2929

3030
// powerbi.extensibility.utils.interactivity
31-
import {interactivityService} from "powerbi-visuals-utils-interactivityutils";
31+
import { interactivityService } from "powerbi-visuals-utils-interactivityutils";
3232
import IInteractiveBehavior = interactivityService.IInteractiveBehavior;
3333
import ISelectionHandler = interactivityService.ISelectionHandler;
3434
import IInteractivityService = interactivityService.IInteractivityService;
3535
import { StreamGraphSeries } from "./dataInterfaces";
36-
import {getFillOpacity} from "./utils";
36+
import { getFillOpacity } from "./utils";
3737

3838
export interface BehaviorOptions {
3939
selection: Selection<d3.BaseType, StreamGraphSeries, any, any>;

src/dataInterfaces.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,8 @@ export interface StreamDataPoint extends IDataLabelInfo {
8282
y0?: number;
8383
text: string;
8484
labelFontSize: string;
85+
value?: number;
86+
highlight?: boolean;
8587
}
8688

8789
export interface StreamGraphSeries extends SelectableDataPoint {

src/visual.ts

Lines changed: 29 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -337,7 +337,8 @@ export class StreamGraph implements IVisual {
337337
? y
338338
: StreamGraph.DefaultValue,
339339
text: label,
340-
labelFontSize: fontSizeInPx
340+
labelFontSize: fontSizeInPx,
341+
highlight: hasHighlights && values[valueIndex].highlights && values[valueIndex].highlights[dataPointValueIndex] !== null
341342
};
342343

343344
series[valueIndex].dataPoints.push(streamDataPoint);
@@ -351,6 +352,10 @@ export class StreamGraph implements IVisual {
351352
}
352353
stackValues[dataPointValueIndex][label] = streamDataPoint.y;
353354

355+
if (!stackValues[dataPointValueIndex]["highlight"]) {
356+
stackValues[dataPointValueIndex]["highlight"] = streamDataPoint.highlight ? 1 : 0;
357+
}
358+
354359
if (streamDataPoint.x > xMaxValue) {
355360
xMaxValue = streamDataPoint.x;
356361
}
@@ -413,8 +418,6 @@ export class StreamGraph implements IVisual {
413418
let xAxisFontSize: number = +settings.categoryAxis.fontSize;
414419
let xAxisFontHalfSize: number = xAxisFontSize / 2;
415420

416-
417-
418421
/* Generate stack values for d3.stack V5 */
419422
const allLabels = legendData.dataPoints.map((dataPoint) => dataPoint.label);
420423

@@ -514,6 +517,10 @@ export class StreamGraph implements IVisual {
514517

515518
this.clearCatcher = appendClearCatcher(this.svg);
516519

520+
this.dataPointsContainer = this.svg
521+
.insert("g")
522+
.classed(StreamGraph.DataPointsContainer, true);
523+
517524
this.axes = this.svg
518525
.append("g")
519526
.classed(StreamGraph.Axes.className, true)
@@ -528,10 +535,6 @@ export class StreamGraph implements IVisual {
528535
.classed(StreamGraph.Axis.className, true)
529536
.classed(StreamGraph.YAxis.className, true);
530537

531-
this.dataPointsContainer = this.svg
532-
.append("g")
533-
.classed(StreamGraph.DataPointsContainer, true);
534-
535538
this.behavior = new StreamGraphBehavior();
536539

537540
this.interactivityService = createInteractivityService(this.visualHost);
@@ -580,10 +583,14 @@ export class StreamGraph implements IVisual {
580583
this.svg.attr("width", PixelConverter.toString(this.viewport.width));
581584
this.svg.attr("height", PixelConverter.toString(this.viewport.height));
582585

586+
const values: DataViewValueColumns = this.dataView.categorical.values;
587+
const hasHighlights: boolean = !!(values.length > 0 && values[0].highlights);
588+
583589
const selection: Selection<d3.BaseType, StreamGraphSeries, any, any> = this.renderChart(
584590
this.data.series,
585591
this.data.stackedSeries,
586-
StreamGraph.AnimationDuration
592+
StreamGraph.AnimationDuration,
593+
hasHighlights
587594
);
588595

589596
this.calculateAxes();
@@ -899,7 +906,8 @@ export class StreamGraph implements IVisual {
899906
private renderChart(
900907
series: StreamGraphSeries[],
901908
stackedSeries: d3.Series<any, any>[],
902-
duration: number
909+
duration: number,
910+
hasHighlights: boolean = false
903911
): Selection<d3.BaseType, StreamGraphSeries, any, any> {
904912

905913
const { width, height } = this.viewport;
@@ -987,6 +995,7 @@ export class StreamGraph implements IVisual {
987995
stackedSeries.forEach((seriesItem: d3.Series<any, any>) => {
988996
let filteredDataPoints: any[];
989997

998+
990999
filteredDataPoints = seriesItem.filter((dataPoint: any) => {
9911000
return dataPoint && dataPoint[0] !== null && dataPoint[0] !== undefined;
9921001
}).map((dataPoint: any) => {
@@ -995,7 +1004,8 @@ export class StreamGraph implements IVisual {
9951004
y0: dataPoint[0],
9961005
y: dataPoint[1],
9971006
text: seriesItem.key,
998-
value: dataPoint.data[seriesItem.key]
1007+
value: dataPoint.data[seriesItem.key],
1008+
highlight: dataPoint.data.highlight
9991009
};
10001010
});
10011011

@@ -1005,10 +1015,18 @@ export class StreamGraph implements IVisual {
10051015
});
10061016

10071017
const viewport: IViewport = {
1008-
height: height - (this.margin.top + this.data.yAxisFontHalfSize) - margin.bottom,
1018+
height: height - (this.margin.top + this.data.yAxisFontHalfSize),
10091019
width: width - (this.margin.right + this.data.xAxisValueMaxTextHalfSize) - margin.left,
10101020
};
10111021

1022+
if (hasHighlights) {
1023+
const highlightedPointArray: StreamDataPoint[] = dataPointsArray.filter((d: StreamDataPoint) => d.highlight && d.value !== StreamGraph.DefaultValue);
1024+
const additionalPointsArray: StreamDataPoint[] = dataPointsArray.filter((d: StreamDataPoint) => highlightedPointArray[0] && d.text === highlightedPointArray[0].text && d.x < highlightedPointArray[0].x);
1025+
dataPointsArray = additionalPointsArray.concat(highlightedPointArray);
1026+
}
1027+
1028+
dataLabelUtils.cleanDataLabels(this.svg);
1029+
10121030
const labels: Selection<d3.BaseType, StreamDataPoint, any, any> =
10131031
dataLabelUtils.drawDefaultLabelsForDataPointChart(
10141032
dataPointsArray,

test.webpack.config.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ const webpack = require("webpack");
33

44
module.exports = {
55
devtool: 'source-map',
6+
mode: 'development',
67
module: {
78
rules: [
89
{

test/visualData.ts

Lines changed: 82 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -32,36 +32,55 @@ import { ValueType } from "powerbi-visuals-utils-typeutils/lib/valueType";
3232
// powerbi.extensibility.utils.test
3333
import { getRandomNumbers } from "powerbi-visuals-utils-testutils";
3434
import { TestDataViewBuilder, TestDataViewBuilderCategoryColumnOptions } from "powerbi-visuals-utils-testutils/lib/dataViewBuilder/testDataViewBuilder";
35+
import { DataViewBuilderValuesColumnOptions } from "powerbi-visuals-utils-testutils/lib/dataViewBuilder/dataViewBuilder";
3536
import { getRandomUniqueSortedDates } from "./helpers/helpers";
3637

38+
const maxValue: number = 100;
39+
3740
export class ProductSalesByDateData extends TestDataViewBuilder {
3841
private static DefaultFormat: string = "$0,000.00";
3942
private static DefaultDateFormat: string = "dddd MMMM d yyyy";
4043
private static DefaultGroupName: string = "Product";
4144

4245
public static ColumnCategory: string = "Date";
4346
public static GroupCategory: string = "Group";
44-
public static ColumnValues1: string = "Product sales 1";
45-
public static ColumnValues2: string = "Product sales 2";
46-
public static ColumnValues3: string = "Product sales 3";
47-
public static ColumnValues4: string = "Product sales 4";
47+
public static GroupNames: string[] = ["Product 1", "Product 2", "Product 3", "Product 4"];
48+
public static ColumnValues: string[] = ["Product sales 1", "Product sales 2", "Product sales 3", "Product sales 4"];
4849

4950
public valuesDate: Date[] = getRandomUniqueSortedDates(
5051
50,
5152
new Date(2014, 0, 1),
5253
new Date(2015, 5, 10));
5354

54-
public valuesSales1: number[] = getRandomNumbers(this.valuesDate.length);
55-
public valuesSales2: number[] = getRandomNumbers(this.valuesDate.length);
56-
public valuesSales3: number[] = getRandomNumbers(this.valuesDate.length);
57-
public valuesSales4: number[] = getRandomNumbers(this.valuesDate.length);
55+
public valuesSales: [number[], number[], number[], number[]] = [
56+
getRandomNumbers(this.valuesDate.length, -maxValue, maxValue),
57+
getRandomNumbers(this.valuesDate.length, -maxValue, maxValue),
58+
getRandomNumbers(this.valuesDate.length, -maxValue, maxValue),
59+
getRandomNumbers(this.valuesDate.length, -maxValue, maxValue)
60+
];
5861

5962
public groups: string[] = [
6063
"FirstGroup",
6164
"SecondGroup"
6265
];
6366

64-
public getDataView(columnNames?: string[], isGroupsEnabled: boolean = false): DataView {
67+
public generateHightLightedValues(valuesArray: number[], hightlightedElementNumber?: number): number[] {
68+
let array: number[] = [];
69+
const lenght: number = valuesArray.length;
70+
for (let i: number = 0; i < lenght; i++) {
71+
array[i] = null;
72+
}
73+
if (!hightlightedElementNumber)
74+
return array;
75+
if (hightlightedElementNumber >= lenght || hightlightedElementNumber < 0) {
76+
array[0] = valuesArray[0];
77+
} else {
78+
array[hightlightedElementNumber] = valuesArray[hightlightedElementNumber];
79+
}
80+
return array;
81+
}
82+
83+
public getDataView(columnNames?: string[], isGroupsEnabled: boolean = false, withHighlights: boolean = false, hightlightedIndex: number = 0, hightlightedElementNumber: number = 0): DataView {
6584
const categoriesColumn: TestDataViewBuilderCategoryColumnOptions[] = [{
6685
source: {
6786
displayName: ProductSalesByDateData.ColumnCategory,
@@ -82,48 +101,62 @@ export class ProductSalesByDateData extends TestDataViewBuilder {
82101
});
83102
}
84103

104+
let columns: DataViewBuilderValuesColumnOptions[] = [{
105+
source: {
106+
displayName: ProductSalesByDateData.ColumnValues[0],
107+
isMeasure: true,
108+
format: ProductSalesByDateData.DefaultFormat,
109+
groupName: ProductSalesByDateData.DefaultGroupName,
110+
type: ValueType.fromDescriptor({ numeric: true })
111+
},
112+
values: this.valuesSales[0]
113+
}, {
114+
source: {
115+
displayName: ProductSalesByDateData.ColumnValues[1],
116+
isMeasure: true,
117+
format: ProductSalesByDateData.DefaultFormat,
118+
groupName: ProductSalesByDateData.DefaultGroupName,
119+
type: ValueType.fromDescriptor({ numeric: true })
120+
},
121+
values: this.valuesSales[1]
122+
}, {
123+
source: {
124+
displayName: ProductSalesByDateData.ColumnValues[2],
125+
isMeasure: true,
126+
format: ProductSalesByDateData.DefaultFormat,
127+
groupName: ProductSalesByDateData.DefaultGroupName,
128+
type: ValueType.fromDescriptor({ numeric: true })
129+
},
130+
values: this.valuesSales[2]
131+
}, {
132+
source: {
133+
displayName: ProductSalesByDateData.ColumnValues[3],
134+
isMeasure: true,
135+
format: ProductSalesByDateData.DefaultFormat,
136+
groupName: ProductSalesByDateData.DefaultGroupName,
137+
type: ValueType.fromDescriptor({ numeric: true })
138+
},
139+
values: this.valuesSales[3]
140+
}];
141+
142+
if (withHighlights) {
143+
columns[hightlightedIndex].highlights = this.generateHightLightedValues(this.valuesSales[hightlightedIndex], hightlightedElementNumber);
144+
columns[hightlightedIndex].source.groupName = ProductSalesByDateData.GroupNames[hightlightedIndex];
145+
146+
for (let i = 0; i < columns.length; i++) {
147+
if (i !== hightlightedIndex) {
148+
columns[i].highlights = this.generateHightLightedValues(this.valuesSales[i]);
149+
columns[i].source.groupName = ProductSalesByDateData.GroupNames[i];
150+
}
151+
}
152+
}
153+
85154
return this.createCategoricalDataViewBuilder(
86155
categoriesColumn, [
87-
{
88-
source: {
89-
displayName: ProductSalesByDateData.ColumnValues1,
90-
isMeasure: true,
91-
format: ProductSalesByDateData.DefaultFormat,
92-
groupName: ProductSalesByDateData.DefaultGroupName,
93-
type: ValueType.fromDescriptor({ numeric: true })
94-
},
95-
values: this.valuesSales1
96-
},
97-
{
98-
source: {
99-
displayName: ProductSalesByDateData.ColumnValues2,
100-
isMeasure: true,
101-
format: ProductSalesByDateData.DefaultFormat,
102-
groupName: ProductSalesByDateData.DefaultGroupName,
103-
type: ValueType.fromDescriptor({ numeric: true })
104-
},
105-
values: this.valuesSales1
106-
},
107-
{
108-
source: {
109-
displayName: ProductSalesByDateData.ColumnValues3,
110-
isMeasure: true,
111-
format: ProductSalesByDateData.DefaultFormat,
112-
groupName: ProductSalesByDateData.DefaultGroupName,
113-
type: ValueType.fromDescriptor({ numeric: true })
114-
},
115-
values: this.valuesSales2
116-
},
117-
{
118-
source: {
119-
displayName: ProductSalesByDateData.ColumnValues4,
120-
isMeasure: true,
121-
format: ProductSalesByDateData.DefaultFormat,
122-
groupName: ProductSalesByDateData.DefaultGroupName,
123-
type: ValueType.fromDescriptor({ numeric: true })
124-
},
125-
values: this.valuesSales3
126-
}
156+
columns[0],
157+
columns[1],
158+
columns[2],
159+
columns[3]
127160
], columnNames).build();
128161
}
129162
}

0 commit comments

Comments
 (0)