Sorting and axes representation issues fix (#29)

This commit is contained in:
Elena Rodionova 2018-08-24 17:34:49 +03:00 коммит произвёл Ignat Vilesov
Родитель c7802e675a
Коммит 3f2d3b3e1a
12 изменённых файлов: 2061 добавлений и 1834 удалений

4
.vscode/settings.json поставляемый
Просмотреть файл

@ -21,13 +21,13 @@
"fileMatch": [
"/pbiviz.json"
],
"url": "./.api/v2.1.0/schema.pbiviz.json"
"url": "./.api/v1.13.0/schema.pbiviz.json"
},
{
"fileMatch": [
"/capabilities.json"
],
"url": "./.api/v12.1.0/schema.capabilities.json"
"url": "./.api/v1.13.0/schema.capabilities.json"
}
]
}

Просмотреть файл

@ -1,6 +1,6 @@
## 1.6.0
* API 2.1.0
## 1.5.1
* Fixed wrong chart's scale yAxis rendering
* Fixed bug with not displayed lines and reverse order animation, when data is sorted descending
## 1.5.0
* High contrast mode
* API 1.13.0

Просмотреть файл

@ -66,6 +66,9 @@
}
}],
"supportsHighlight": true,
"sorting": {
"default": {}
},
"objects": {
"lineoptions": {
"displayName": "Line",

3522
package-lock.json сгенерированный

Разница между файлами не показана из-за своего большого размера Загрузить разницу

Просмотреть файл

@ -1,13 +1,13 @@
{
"name": "powerbi-visuals-linedotchart",
"description": "LineDot Chart",
"version": "1.6.0",
"version": "1.5.1",
"author": {
"name": "Microsoft",
"email": "pbicvsupport@microsoft.com"
},
"scripts": {
"postinstall": "pbiviz update 2.1.0",
"postinstall": "pbiviz update 1.13.0",
"pbiviz": "pbiviz",
"start": "pbiviz start",
"package": "pbiviz package",
@ -30,6 +30,7 @@
"powerbi-visuals-utils-colorutils": "1.1.0",
"powerbi-visuals-utils-dataviewutils": "1.4.1",
"powerbi-visuals-utils-interactivityutils": "3.2.0",
"powerbi-visuals-utils-formattingutils": "3.0.1",
"powerbi-visuals-utils-tooltiputils": "1.0.1"
},
"devDependencies": {
@ -49,7 +50,7 @@
"karma-remap-istanbul": "0.6.0",
"karma-sourcemap-loader": "0.3.7",
"karma-typescript-preprocessor": "0.3.1",
"powerbi-visuals-tools": "2.1.0",
"powerbi-visuals-tools": "1.13.0",
"powerbi-visuals-utils-testutils": "1.2.1",
"puppeteer": "^1.6.1",
"tslint": "3.15.1",

Просмотреть файл

@ -1,15 +1,15 @@
{
"visual": {
"name": "LineDotChart",
"displayName": "LineDot Chart 1.6.0",
"displayName": "LineDot Chart 1.5.1",
"guid": "LineDotChart1460463831201",
"visualClassName": "LineDotChart",
"version": "1.6.0",
"version": "1.5.1",
"description": "The LineDot chart is an animated line chart with fun animated dots. Use the LineDot chart to engage your audience especially in a presentation context. The bubbles size can be dynamic based on data you provide. A counter is provided that you can use to show a running value as the chart animates. Format options are provided for Lines, Dots, and Animation.",
"supportUrl": "https://community.powerbi.com",
"gitHubUrl": "https://github.com/Microsoft/powerbi-visuals-linedotchart"
},
"apiVersion": "2.1.0",
"apiVersion": "1.13.0",
"author": {
"name": "Microsoft",
"email": "pbicvsupport@microsoft.com"

Просмотреть файл

@ -119,8 +119,33 @@ module powerbi.extensibility.visual {
height: 150
};
private tooltipServiceWrapper: ITooltipServiceWrapper;
public static columnFormattingFn(data: LineDotChartViewModel) {
return function (index: number, dataType: valueType): any {
if (dataType.dateTime) {
return data.dateColumnFormatter.format(new Date(index));
}
else if (dataType.text) {
return data.dateValues[index].label;
}
return data.dateColumnFormatter.format(index);
};
}
public static valueFormattingFn(data: LineDotChartViewModel) {
return function (index: number, dataType: valueType): any {
if (dataType.dateTime) {
return data.dataValueFormatter.format(new Date(index));
}
else if (dataType.text) {
return data.dateValues[index].label;
}
let formatted: string = data.dataValueFormatter.format(index);
return formatted !== index.toString() ? formatted : index;
};
}
private tooltipServiceWrapper: ITooltipServiceWrapper;
private colorHelper: ColorHelper;
constructor(options: VisualConstructorOptions) {
@ -130,12 +155,10 @@ module powerbi.extensibility.visual {
);
this.colorHelper = new ColorHelper(options.host.colorPalette);
this.hostService = options.host;
this.localizationManager = this.hostService.createLocalizationManager();
this.layout = new VisualLayout(null, LineDotChart.viewportMargins);
this.layout.minViewport = LineDotChart.viewportDimensions;
this.interactivityService = createInteractivityService(options.host);
@ -375,7 +398,8 @@ module powerbi.extensibility.visual {
};
const dataValueFormatter: IValueFormatter = valueFormatter.create({
format: valueFormatter.getFormatStringByColumn(valuesColumn.source)
format: valueFormatter.getFormatStringByColumn(valuesColumn.source, true) || "#",
cultureSelector: visualHost.locale
});
return {
@ -412,8 +436,8 @@ module powerbi.extensibility.visual {
this.data.dateValues,
(dateValue: DateValue) => dateValue.value);
const minDate: number = extentDate[0];
const maxDate: number = extentDate[1] + (extentDate[1] - extentDate[0]) * LineDotChart.dateMaxCutter;
let minDate: number = extentDate[0],
maxDate: number = extentDate[1] + (extentDate[1] - extentDate[0]) * LineDotChart.dateMaxCutter;
this.xAxisProperties = AxisHelper.createAxis({
pixelSpan: effectiveWidth,
@ -430,15 +454,7 @@ module powerbi.extensibility.visual {
forcedTickCount: Math.max(this.layout.viewport.width / LineDotChart.forcedTickSize, 0),
useTickIntervalForDisplayUnits: false,
shouldClamp: true,
getValueFn: (index: number, dataType: valueType): any => {
if (dataType.dateTime) {
return this.data.dateColumnFormatter.format(new Date(index));
}
else if (dataType.text) {
return this.data.dateValues[index].label;
}
return index;
}
getValueFn: LineDotChart.columnFormattingFn(this.data)
});
this.xAxisProperties.xLabelMaxWidth = Math.min(
@ -447,29 +463,47 @@ module powerbi.extensibility.visual {
);
this.xAxisProperties.formatter = this.data.dateColumnFormatter;
let yMin = this.data.yMinValue;
let yMax = this.data.yMaxValue;
// Expanding a scope by increasing yMin and yMax to render y-axes
// - if all data values are the same (yMin = yMax) we increasing them all, for floats - increasing by const float, for integers - by 1;
// - if the data has diffrent minimum and maximum values we increasing only yMax
if (yMax === yMin) {
if ((Math.floor(yMin) === yMin) && yMin !== 0) {
yMin = yMin - 1;
yMax = yMax + 1;
} else {
yMin = yMin - LineDotChart.dateMaxCutter;
yMax = yMax + LineDotChart.dateMaxCutter;
}
} else {
yMax = yMax + (yMax - yMin) * LineDotChart.makeSomeSpaceForCounter;
}
this.yAxisProperties = AxisHelper.createAxis({
pixelSpan: effectiveHeight,
dataDomain: [this.data.yMinValue, this.data.sumOfValues],
dataDomain: [yMin, yMax],
metaDataColumn: this.data.valuesMetadataColumn,
formatString: null,
outerPadding: LineDotChart.outerPadding,
isCategoryAxis: false,
isScalar: true,
isVertical: true,
useTickIntervalForDisplayUnits: true
useTickIntervalForDisplayUnits: true,
getValueFn: LineDotChart.valueFormattingFn(this.data)
});
this.yAxis2Properties = AxisHelper.createAxis({
pixelSpan: effectiveHeight,
dataDomain: [this.data.yMinValue, this.data.sumOfValues],
dataDomain: [yMin, yMax],
metaDataColumn: this.data.valuesMetadataColumn,
formatString: null,
outerPadding: LineDotChart.outerPadding,
isCategoryAxis: false,
isScalar: true,
isVertical: true,
useTickIntervalForDisplayUnits: true
useTickIntervalForDisplayUnits: true,
getValueFn: LineDotChart.valueFormattingFn(this.data)
});
this.yAxis2Properties.axis.orient("right");
@ -772,7 +806,7 @@ module powerbi.extensibility.visual {
return this.xAxisProperties.scale(dataPoint.dateValue.value);
})
.y((dataPoint: LineDotPoint) => {
return this.yAxisProperties.scale(dataPoint.sum);
return this.yAxisProperties.scale(dataPoint.value);
});
pathPlot
@ -931,7 +965,7 @@ module powerbi.extensibility.visual {
.attr("transform", (dataPoint: LineDotPoint) => {
return SVGUtil.translateAndScale(
this.xAxisProperties.scale(dataPoint.dateValue.value),
this.yAxisProperties.scale(dataPoint.sum),
this.yAxisProperties.scale(dataPoint.value),
LineDotChart.pointScaleValue);
})
.transition()
@ -958,9 +992,8 @@ module powerbi.extensibility.visual {
.attr("transform", (dataPoint: LineDotPoint) => {
return SVGUtil.translateAndScale(
this.xAxisProperties.scale(dataPoint.dateValue.value),
this.yAxisProperties.scale(dataPoint.sum),
LineDotChart.pointTransformScaleValue
);
this.yAxisProperties.scale(dataPoint.value),
LineDotChart.pointTransformScaleValue);
})
.transition()
.duration(point_time)
@ -971,9 +1004,8 @@ module powerbi.extensibility.visual {
.attr("transform", (dataPoint: LineDotPoint) => {
return SVGUtil.translateAndScale(
this.xAxisProperties.scale(dataPoint.dateValue.value),
this.yAxisProperties.scale(dataPoint.sum),
1
);
this.yAxisProperties.scale(dataPoint.value),
1);
});
} else {
dotsSelection
@ -981,9 +1013,8 @@ module powerbi.extensibility.visual {
.attr("transform", (dataPoint: LineDotPoint) => {
return SVGUtil.translateAndScale(
this.xAxisProperties.scale(dataPoint.dateValue.value),
this.yAxisProperties.scale(dataPoint.sum),
1
);
this.yAxisProperties.scale(dataPoint.value),
1);
});
this.line
@ -1079,9 +1110,8 @@ module powerbi.extensibility.visual {
const unformattedDate: string | number = dataPoint.dateValue.label || dataPoint.dateValue.value;
const valueFormatterLocalized = valueFormatter.create({ cultureSelector: this.hostService.locale });
const formattedDate: string = this.data.dateColumnFormatter.format(unformattedDate);
const formattedValue: string = this.data.dataValueFormatter.format(valueFormatterLocalized.format(dataPoint.value));
const formattedValue: string = this.data.dataValueFormatter.format(dataPoint.value);
const columnNames: ColumnNames = this.data.columnNames;

Просмотреть файл

@ -30,7 +30,7 @@
/// <reference path="../node_modules/@types/jasmine-jquery/index.d.ts" />
// Power BI API
/// <reference path="../.api/v2.1.0/PowerBI-visuals.d.ts" />
/// <reference path="../.api/v1.13.0/PowerBI-visuals.d.ts" />
// Power BI Extensibility
/// <reference path="../node_modules/powerbi-visuals-utils-dataviewutils/lib/index.d.ts" />

Просмотреть файл

@ -69,13 +69,13 @@ module powerbi.extensibility.visual.test {
public get axes() {
return this.mainElement
.children("g")
.children("g.axes");
.children("g")
.children("g.axes");
}
public get axis() {
return this.axes
.children("g.axis");
.children("g.axis");
}
public get emptyAxis() {
@ -87,10 +87,19 @@ module powerbi.extensibility.visual.test {
.children("g.tick");
}
public get xAxisTick() {
return this.axis.first()
.children("g.tick");
}
public get tickText() {
return this.ticks.children("text");
}
public get xAxisTickText() {
return this.xAxisTick.children("text");
}
public get animationPlay(): JQuery {
return this.mainElement
.find("g.lineDotChart__playBtn");

Просмотреть файл

@ -27,6 +27,9 @@
/// <reference path="_references.ts" />
module powerbi.extensibility.visual.test {
// powerbi
import DataView = powerbi.DataView;
// powerbi.extensibility.utils.type
import ValueType = powerbi.extensibility.utils.type.ValueType;
@ -56,67 +59,91 @@ module powerbi.extensibility.visual.test {
export class LineDotChartData extends TestDataViewBuilder {
public static ColumnDate: string = "Date";
public static ColumnValue: string = "Value";
public static DefaultFormat: string = "#";
public static PercentFormat: string = "0%;-0%;0%";
public static PriceFormat: string = "\$#,0.000;(\$#,0.000);\$#,0.000";
public valuesDate: Date[] = getRandomUniqueSortedDates(
50,
new Date(2014, 9, 12, 3, 9, 50),
new Date(2016, 3, 1, 2, 43, 3)
);
public valuesValue = helpers.getRandomNumbers(this.valuesDate.length, 0, 5361);
public valuesForPercentFormat = helpers.getRandomNumbers(this.valuesDate.length, 0, 100);
public valuesDateAsString: string[] = this.valuesDate.map(x => x.toISOString());
public getDataView(columnNames?: string[]): powerbi.DataView {
return this.createDataView(false, columnNames);
public getDataView(columnNames?: string[], valuesDate?: string[] | Date[] | number[], valuesValue?: string[] | Date[] | number[]): DataView {
return this.getFormattedDataView(
ValueType.fromDescriptor({ dateTime: true }),
ValueType.fromDescriptor({ integer: true }),
valuesDate ? valuesDate : this.valuesDate,
valuesValue ? valuesValue : this.valuesValue,
columnNames
);
}
private createDataView(isDateAsString: boolean, columnNames?: string[]): powerbi.DataView {
public getDataViewWithDifferentFormats(columnNames?: string[]): DataView {
return this.getFormattedDataView(
ValueType.fromDescriptor({ numeric: true }),
ValueType.fromDescriptor({ integer: true }),
this.valuesValue,
this.valuesForPercentFormat,
columnNames,
LineDotChartData.PriceFormat,
LineDotChartData.PercentFormat
);
}
private getFormattedDataView(
valueTypeDescriptor1: ValueTypeDescriptor,
valueTypeDescriptor2: ValueTypeDescriptor,
values1: string[] | Date[] | number[],
values2: string[] | Date[] | number[],
columnNames?: string[],
format1: string = LineDotChartData.DefaultFormat,
format2: string = LineDotChartData.DefaultFormat,
): DataView {
return this.createCategoricalDataViewBuilder([
{
source: {
displayName: LineDotChartData.ColumnDate,
type: ValueType.fromDescriptor({ dateTime: true }),
roles: { Date: true }
type: valueTypeDescriptor1,
roles: { Date: true },
format: format1
},
values: isDateAsString ? this.valuesDateAsString : this.valuesDate
values: values1
}
], [
{
source: {
displayName: "Values",
type: ValueType.fromDescriptor({ integer: true }),
roles: { Values: true }
displayName: LineDotChartData.ColumnValue,
type: valueTypeDescriptor2,
roles: { Values: true },
format: format2
},
values: this.valuesValue
values: values2
}
], columnNames).build();
}
public createStringView(columnNames?: string[]): powerbi.DataView {
return this.createCategoricalDataViewBuilder([
{
source: {
displayName: LineDotChartData.ColumnDate,
type: ValueType.fromDescriptor({ text: true }),
roles: { Date: true }
},
values: ["Alpha", "Beta", "Omega", "Gamma"]
}
], [
{
source: {
displayName: "Values",
type: ValueType.fromDescriptor({ integer: true }),
roles: { Values: true }
},
values: [100, 200, 300, 400]
}
], columnNames).build();
return this.getFormattedDataView(
ValueType.fromDescriptor({ text: true }),
ValueType.fromDescriptor({ integer: true }),
["Alpha", "Beta", "Omega", "Gamma"],
[100, 200, 300, 400],
columnNames
);
}
public getDataViewForCategoricalValues(columnNames?: string[]): powerbi.DataView {
return this.createDataView(true, columnNames);
return this.getFormattedDataView(
ValueType.fromDescriptor({ dateTime: true }),
ValueType.fromDescriptor({ integer: true }),
this.valuesDateAsString,
this.valuesValue,
columnNames
);
}
}
}

Просмотреть файл

@ -45,6 +45,7 @@ namespace powerbi.extensibility.visual.test {
import LineDotPoint = powerbi.extensibility.visual.LineDotChart1460463831201.LineDotPoint;
import LineDotChartViewModel = powerbi.extensibility.visual.LineDotChart1460463831201.LineDotChartViewModel;
import LineDotChartColumns = powerbi.extensibility.visual.LineDotChart1460463831201.LineDotChartColumns;
import LineDotChart = powerbi.extensibility.visual.LineDotChart1460463831201.LineDotChart;
describe("LineDotChartTests", () => {
let visualBuilder: LineDotChartBuilder,
@ -459,5 +460,147 @@ namespace powerbi.extensibility.visual.test {
}
});
});
describe("should formatting functions work correctly", () => {
let data: LineDotChartViewModel;
let columnFormattingFn: Function;
let valueFormattingFn: Function;
beforeEach(() => {
dataView = defaultDataViewBuilder.getDataViewWithDifferentFormats();
visualBuilder.update(dataView);
data = visualBuilder.visualInstance.data;
columnFormattingFn = LineDotChart.columnFormattingFn(data);
valueFormattingFn = LineDotChart.valueFormattingFn(data);
});
it("dateTime formatting", () => {
const timestamp: number = 108875;
const actualResultForColumn: string = columnFormattingFn(timestamp, { dateTime: true });
const actualResultForValue: string = valueFormattingFn(timestamp, { dateTime: true });
const expectedResultForColumn: string = data.dateColumnFormatter.format(new Date(timestamp));
const expectedResultForValue: string = data.dataValueFormatter.format(new Date(timestamp));
expect(actualResultForColumn).toBe(expectedResultForColumn);
expect(actualResultForValue).toBe(expectedResultForValue);
});
it("text formatting", () => {
const index: number = 17;
const actualResultForColumn: string = columnFormattingFn(index, { text: true });
const actualResultForValue: string = valueFormattingFn(index, { text: true });
const expectedResult: string = data.dateValues[index].label;
expect(actualResultForColumn).toBe(expectedResult);
expect(actualResultForValue).toBe(expectedResult);
});
it("numbers formatting", () => {
const index: number = 13;
const actualResultForColumn: string = columnFormattingFn(index, { number: true });
const expectedResultForColumn: string = data.dateColumnFormatter.format(index);
const actualResultForValue: string = valueFormattingFn(index, { number: true });
const expectedResultForValue: string = data.dataValueFormatter.format(index);
expect(actualResultForColumn).toBe(expectedResultForColumn);
expect(actualResultForValue).toBe(expectedResultForValue);
});
});
describe("Different formats data representation test", () => {
let tickText: JQuery[];
let xTicksCount: number;
beforeEach(() => {
dataView = defaultDataViewBuilder.getDataViewWithDifferentFormats();
visualBuilder.update(dataView);
tickText = visualBuilder.tickText.toArray().map($);
xTicksCount = visualBuilder.xAxisTickText.toArray().length;
});
it("should representate data in required format on axes", (done) => {
const percentRegex: string = "^\\d+(\.?\\d+)?%$";
const priceRegex: string = "$";
visualBuilder.updateRenderTimeout(dataView, () => {
tickText.forEach((tick, index) => {
let text = tickText[index].text();
if (index < xTicksCount) {
expect(text).toMatch(priceRegex);
} else {
expect(text).toMatch(percentRegex);
}
});
done();
});
});
it("should representate data in required format in tooltip", () => {
const defaultFormattedColumnValue: string = visualBuilder.visualInstance.data.dateColumnFormatter.format(13);
const defaultFormattedValue: string = visualBuilder.visualInstance.data.dataValueFormatter.format(17);
const dataPoint: LineDotPoint = {
dateValue: {
value: 13
},
value: 17
} as LineDotPoint;
const actualResult: VisualTooltipDataItem[]
= visualBuilder.visualInstance.getTooltipDataItems(dataPoint);
expect(actualResult[0].value).toBe(defaultFormattedColumnValue);
expect(actualResult[1].value).toBe(defaultFormattedValue);
});
});
describe("Y axis right scaling test", () => {
let yTicksText: JQuery[] = [];
let allTicksText: JQuery[];
beforeEach(() => {
const orderedDates: Date[] = [
new Date(2013, 1, 1),
new Date(2014, 1, 1),
new Date(2015, 1, 1),
new Date(2016, 1, 1),
new Date(2017, 1, 1)
];
const orderedNumbers: number[] = [11, 18, 23, 29, 31];
dataView = defaultDataViewBuilder.getDataView(undefined, orderedDates, orderedNumbers);
visualBuilder.update(dataView);
let xTicksCount = visualBuilder.xAxisTick.toArray().length;
allTicksText = visualBuilder.tickText.toArray().map($);
const yTicksCount: number = (allTicksText.length - xTicksCount) / 2;
allTicksText.forEach((tick, index) => {
if (index >= xTicksCount && index <= yTicksCount + xTicksCount - 1) {
yTicksText.push(tick);
}
});
});
it("should graphic be correctly scaled on y axis", (done) => {
const dotPoints: LineDotPoint[] = visualBuilder.visualInstance.data.dotPoints;
let previosYTickIndex = 0;
visualBuilder.updateRenderTimeout(dataView, () => {
dotPoints.forEach((dotPoint: LineDotPoint) => {
let lowAxisValue: number = parseInt(yTicksText[previosYTickIndex].text());
expect(dotPoint.value).toBeGreaterThanOrEqual(lowAxisValue);
if (previosYTickIndex + 1 < yTicksText.length) {
let highAxisValue: number = parseInt(yTicksText[previosYTickIndex + 1].text());
expect(dotPoint.value).toBeGreaterThanOrEqual(lowAxisValue);
}
previosYTickIndex++;
});
done();
});
});
});
});
}

Просмотреть файл

@ -9,7 +9,7 @@
"declaration": true
},
"files": [
".api/v2.1.0/PowerBI-visuals.d.ts",
".api/v1.13.0/PowerBI-visuals.d.ts",
"node_modules/powerbi-visuals-utils-formattingutils/lib/index.d.ts",
"node_modules/powerbi-visuals-utils-interactivityutils/lib/index.d.ts",
"node_modules/powerbi-visuals-utils-typeutils/lib/index.d.ts",