Auto Precision + Stale Data options per metric + Subtitle form a Field + Infrastructure changes (#15)
* Auto Precision + Stale Data options per metric + Subtitle form a Field * Infrastructure changes
This commit is contained in:
Родитель
01b059af73
Коммит
b73ba91e5e
|
@ -1,3 +1,12 @@
|
|||
## 2.3.0
|
||||
* API 3.5.1
|
||||
* Packages update
|
||||
* Subtitle can be loaded form data also (will be merged with subtitle from options)
|
||||
* Some of options from Stale Data option group can be set up for a metric
|
||||
* New option "Auto Precision" that build values showing minimum 3 digits as 3.56 or 25.7 or 754 or 2345
|
||||
* Context menu support
|
||||
* FIXED: it is expected to see the line when values are the same but it will be shown a dot in sparklines and nothing in the main chart
|
||||
|
||||
## 2.2.1
|
||||
* Fix for last date visual issue
|
||||
|
||||
|
|
|
@ -24,6 +24,10 @@
|
|||
"displayName": "Change start date",
|
||||
"name": "changeStartDateColumn",
|
||||
"kind": "Grouping"
|
||||
}, {
|
||||
"displayName": "Subtitle",
|
||||
"name": "subtitleColumn",
|
||||
"kind": "Measure"
|
||||
}],
|
||||
"objects": {
|
||||
"date": {
|
||||
|
@ -56,6 +60,12 @@
|
|||
}
|
||||
}
|
||||
},
|
||||
"autoPrecision": {
|
||||
"displayName": "Auto precision",
|
||||
"type": {
|
||||
"bool": true
|
||||
}
|
||||
},
|
||||
"precision": {
|
||||
"displayName": "Precision",
|
||||
"type": {
|
||||
|
@ -101,6 +111,12 @@
|
|||
}
|
||||
}
|
||||
},
|
||||
"autoPrecision": {
|
||||
"displayName": "Auto precision",
|
||||
"type": {
|
||||
"bool": true
|
||||
}
|
||||
},
|
||||
"precision": {
|
||||
"displayName": "Precision",
|
||||
"type": {
|
||||
|
@ -171,6 +187,12 @@
|
|||
}
|
||||
}
|
||||
},
|
||||
"autoPrecision": {
|
||||
"displayName": "Auto precision",
|
||||
"type": {
|
||||
"bool": true
|
||||
}
|
||||
},
|
||||
"precision": {
|
||||
"displayName": "Precision",
|
||||
"type": {
|
||||
|
@ -733,6 +755,12 @@
|
|||
}
|
||||
}
|
||||
},
|
||||
"autoPrecision": {
|
||||
"displayName": "Auto precision",
|
||||
"type": {
|
||||
"bool": true
|
||||
}
|
||||
},
|
||||
"precision": {
|
||||
"displayName": "Precision",
|
||||
"type": {
|
||||
|
@ -789,6 +817,12 @@
|
|||
}
|
||||
}
|
||||
},
|
||||
"autoPrecision": {
|
||||
"displayName": "Auto precision",
|
||||
"type": {
|
||||
"bool": true
|
||||
}
|
||||
},
|
||||
"precision": {
|
||||
"displayName": "Precision",
|
||||
"type": {
|
||||
|
@ -893,7 +927,7 @@
|
|||
"displayName": "Stale Data",
|
||||
"description": "To turn on Stale Data make sure to turn on Subtitle first",
|
||||
"properties": {
|
||||
"show": {
|
||||
"isShown": {
|
||||
"displayName": "Show",
|
||||
"type": {
|
||||
"bool": true
|
||||
|
@ -920,7 +954,7 @@
|
|||
}
|
||||
},
|
||||
"color": {
|
||||
"displayName": "Color",
|
||||
"displayName": "Color (for all only)",
|
||||
"type": {
|
||||
"fill": {
|
||||
"solid": {
|
||||
|
@ -930,7 +964,7 @@
|
|||
}
|
||||
},
|
||||
"background": {
|
||||
"displayName": "Background Color",
|
||||
"displayName": "Background Color (for all only)",
|
||||
"type": {
|
||||
"fill": {
|
||||
"solid": {
|
||||
|
@ -972,6 +1006,9 @@
|
|||
},
|
||||
"changeStartDateColumn": {
|
||||
"max": 0
|
||||
},
|
||||
"subtitleColumn": {
|
||||
"max": 0
|
||||
}
|
||||
}, {
|
||||
"dateColumn": {
|
||||
|
@ -990,6 +1027,9 @@
|
|||
},
|
||||
"changeStartDateColumn": {
|
||||
"max": 1
|
||||
},
|
||||
"subtitleColumn": {
|
||||
"max": 1
|
||||
}
|
||||
}],
|
||||
"categorical": {
|
||||
|
@ -1006,6 +1046,10 @@
|
|||
"for": {
|
||||
"in": "changeStartDateColumn"
|
||||
}
|
||||
}, {
|
||||
"for": {
|
||||
"in": "subtitleColumn"
|
||||
}
|
||||
}],
|
||||
"dataReductionAlgorithm": {
|
||||
"top": {
|
||||
|
@ -1033,5 +1077,6 @@
|
|||
"direction": 1
|
||||
}]
|
||||
}
|
||||
}
|
||||
},
|
||||
"supportsMultiVisualSelection": true
|
||||
}
|
||||
|
|
|
@ -32,13 +32,10 @@ const path = require("path");
|
|||
|
||||
const webpackConfig = require("./test.webpack.config.js");
|
||||
const tsconfig = require("./tsconfig.json");
|
||||
|
||||
import { Config, ConfigOptions } from "karma";
|
||||
|
||||
const testRecursivePath = "specs/*.spec.ts";
|
||||
const coverageFolder = "coverage";
|
||||
|
||||
module.exports = (config: Config) => {
|
||||
module.exports = (config) => {
|
||||
config.set({
|
||||
browsers: ["ChromeHeadless"],
|
||||
colors: true,
|
||||
|
@ -92,5 +89,5 @@ module.exports = (config: Config) => {
|
|||
webpackMiddleware: {
|
||||
noInfo: true,
|
||||
},
|
||||
} as ConfigOptions);
|
||||
});
|
||||
};
|
||||
|
|
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
76
package.json
76
package.json
|
@ -1,12 +1,13 @@
|
|||
{
|
||||
"name": "powerbi-visuals-multikpi",
|
||||
"version": "2.2.1",
|
||||
"version": "2.3.0",
|
||||
"description": "Shows a KPI metric along with other metrics as sparklines",
|
||||
"scripts": {
|
||||
"start": "pbiviz start",
|
||||
"package": "pbiviz package",
|
||||
"test": "karma start",
|
||||
"watch": "karma start --single-run=false"
|
||||
"watch": "karma start --single-run=false",
|
||||
"cert": "pbiviz --install-cert"
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
|
@ -27,50 +28,51 @@
|
|||
},
|
||||
"homepage": "https://github.com/Microsoft/PowerBI-visuals-MultiKPI#readme",
|
||||
"devDependencies": {
|
||||
"@types/d3": "5.7.2",
|
||||
"@types/jasmine": "3.4.4",
|
||||
"@types/d3": "6.2.0",
|
||||
"@types/jasmine": "3.6.2",
|
||||
"@types/jasmine-jquery": "1.5.33",
|
||||
"@types/jquery": "3.3.31",
|
||||
"@types/karma": "^4.4.1",
|
||||
"@types/puppeteer": "1.20.2",
|
||||
"core-js": "3.3.3",
|
||||
"css-loader": "3.2.0",
|
||||
"@types/jquery": "3.5.5",
|
||||
"@types/karma": "5.0.1",
|
||||
"@types/puppeteer": "5.4.2",
|
||||
"core-js": "3.8.1",
|
||||
"css-loader": "5.0.1",
|
||||
"istanbul-instrumenter-loader": "3.0.1",
|
||||
"jasmine": "3.5.0",
|
||||
"jasmine": "3.6.3",
|
||||
"jasmine-jquery": "2.1.1",
|
||||
"jquery": "3.4.1",
|
||||
"karma": "4.4.1",
|
||||
"jquery": "3.5.1",
|
||||
"karma": "5.2.3",
|
||||
"karma-chrome-launcher": "3.1.0",
|
||||
"karma-coverage-istanbul-reporter": "2.1.0",
|
||||
"karma-jasmine": "2.0.1",
|
||||
"karma-coverage-istanbul-reporter": "3.0.3",
|
||||
"karma-jasmine": "4.0.1",
|
||||
"karma-junit-reporter": "2.0.1",
|
||||
"karma-sourcemap-loader": "0.3.7",
|
||||
"karma-sourcemap-loader": "0.3.8",
|
||||
"karma-webpack": "4.0.2",
|
||||
"less": "3.10.3",
|
||||
"less-loader": "5.0.0",
|
||||
"mini-css-extract-plugin": "0.8.0",
|
||||
"powerbi-visuals-api": "2.6.1",
|
||||
"powerbi-visuals-tools": "^3.1.12",
|
||||
"powerbi-visuals-utils-testutils": "2.2.1",
|
||||
"puppeteer": "1.13.0",
|
||||
"style-loader": "1.0.0",
|
||||
"ts-loader": "6.2.1",
|
||||
"ts-node": "8.4.1",
|
||||
"tslint": "5.20.0",
|
||||
"less": "4.0.0",
|
||||
"less-loader": "^5.0.0",
|
||||
"mini-css-extract-plugin": "1.3.3",
|
||||
"powerbi-visuals-api": "3.5.1",
|
||||
"powerbi-visuals-tools": "3.1.15",
|
||||
"powerbi-visuals-utils-testutils": "2.3.4",
|
||||
"puppeteer": "5.5.0",
|
||||
"style-loader": "2.0.0",
|
||||
"ts-loader": "8.0.12",
|
||||
"ts-node": "9.1.1",
|
||||
"tslint": "6.1.1",
|
||||
"tslint-loader": "3.5.4",
|
||||
"tslint-microsoft-contrib": "6.2.0",
|
||||
"typescript": "3.6.4",
|
||||
"webpack": "4.41.2"
|
||||
"typescript": "4.1.3",
|
||||
"webpack": "^4.44.2"
|
||||
},
|
||||
"dependencies": {
|
||||
"d3": "5.12.0",
|
||||
"powerbi-visuals-utils-chartutils": "2.4.1",
|
||||
"powerbi-visuals-utils-colorutils": "2.2.1",
|
||||
"powerbi-visuals-utils-dataviewutils": "2.2.1",
|
||||
"powerbi-visuals-utils-formattingutils": "4.4.2",
|
||||
"powerbi-visuals-utils-interactivityutils": "5.6.0",
|
||||
"powerbi-visuals-utils-svgutils": "2.2.1",
|
||||
"powerbi-visuals-utils-tooltiputils": "2.3.1",
|
||||
"powerbi-visuals-utils-typeutils": "2.2.1"
|
||||
"d3": "6.3.1",
|
||||
"powerbi-visuals-utils-chartutils": "2.5.0",
|
||||
"powerbi-visuals-utils-colorutils": "2.3.0",
|
||||
"powerbi-visuals-utils-dataviewutils": "2.4.0",
|
||||
"powerbi-visuals-utils-formattingutils": "4.7.0",
|
||||
"powerbi-visuals-utils-interactivityutils": "5.7.0",
|
||||
"powerbi-visuals-utils-svgutils": "2.3.0",
|
||||
"powerbi-visuals-utils-tooltiputils": "^2.5.1",
|
||||
"powerbi-visuals-utils-typeutils": "2.3.0",
|
||||
"regenerator-runtime": "^0.13.7"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,10 +1,10 @@
|
|||
{
|
||||
"visual": {
|
||||
"name": "MultiKpi",
|
||||
"displayName": "Multi KPI 2.2.1",
|
||||
"displayName": "Multi KPI 2.3.0",
|
||||
"guid": "multiKpiEA8DA325489E436991F0E411F2D85FF3",
|
||||
"visualClassName": "MultiKpi",
|
||||
"version": "2.2.1",
|
||||
"version": "2.3.0",
|
||||
"description": "Shows a KPI metric along with other metrics as sparklines",
|
||||
"supportUrl": "https://aka.ms/customvisualscommunity",
|
||||
"gitHubUrl": "https://github.com/Microsoft/PowerBI-visuals-MultiKPI"
|
||||
|
|
|
@ -28,7 +28,7 @@ import "jasmine-jquery";
|
|||
|
||||
import * as $ from "jquery";
|
||||
|
||||
import powerbi from "powerbi-visuals-api";
|
||||
import powerbiVisualsApi from "powerbi-visuals-api";
|
||||
|
||||
import { dispatch } from "d3-dispatch";
|
||||
import { select as d3Select, Selection } from "d3-selection";
|
||||
|
@ -55,7 +55,7 @@ import {
|
|||
IDataRepresentationPoint,
|
||||
} from "../src/converter/data/dataRepresentation";
|
||||
|
||||
import { isValueValid } from "../src/utils/valueUtils";
|
||||
import { isValueValid } from "../src/utils/isValueValid";
|
||||
|
||||
import { DataConverter } from "../src/converter/data/dataConverter";
|
||||
import { getFormattedValueWithFallback } from "../src/converter/data/dataFormatter";
|
||||
|
@ -64,11 +64,457 @@ import { KpiBaseDescriptor } from "../src/settings/descriptors/kpi/kpiBaseDescri
|
|||
import { NumericDescriptor } from "../src/settings/descriptors/numericDescriptor";
|
||||
|
||||
import { TestWrapper } from "./testWrapper";
|
||||
import { SubtitleWarningComponent } from "../src/visualComponent/subtitleWarningComponent";
|
||||
|
||||
describe("Multi KPI", () => {
|
||||
describe("Version 2.3.0 Changes", () => {
|
||||
describe("DataFormatter", () => {
|
||||
it("should return N/A if a variance is not valid", () => {
|
||||
const numericDescriptor: NumericDescriptor = new NumericDescriptor();
|
||||
numericDescriptor.autoPrecision = true;
|
||||
expect(getFormattedValueWithFallback(NaN, numericDescriptor)).toBe("N/A");
|
||||
});
|
||||
|
||||
it("should return 12.3K for 12340 if precision is auto and display units are auto", () => {
|
||||
const value: number = 12340;
|
||||
const expectedValue: string = "12.3K";
|
||||
const numericDescriptor: NumericDescriptor = new NumericDescriptor();
|
||||
numericDescriptor.autoPrecision = true;
|
||||
numericDescriptor.precision = 5;
|
||||
|
||||
const actualValue: string = getFormattedValueWithFallback(
|
||||
value,
|
||||
numericDescriptor,
|
||||
);
|
||||
|
||||
expect(actualValue).toBe(expectedValue);
|
||||
});
|
||||
|
||||
it("should return 2.34K for 2340 if precision is auto and display units are auto", () => {
|
||||
const value: number = 2340;
|
||||
const expectedValue: string = "2.34K";
|
||||
const numericDescriptor: NumericDescriptor = new NumericDescriptor();
|
||||
numericDescriptor.autoPrecision = true;
|
||||
numericDescriptor.precision = 5;
|
||||
|
||||
const actualValue: string = getFormattedValueWithFallback(
|
||||
value,
|
||||
numericDescriptor,
|
||||
);
|
||||
|
||||
expect(actualValue).toBe(expectedValue);
|
||||
});
|
||||
|
||||
it("should return 123K for 123403 if precision is auto and display units are auto", () => {
|
||||
const value: number = 123403;
|
||||
const expectedValue: string = "123K";
|
||||
const numericDescriptor: NumericDescriptor = new NumericDescriptor();
|
||||
numericDescriptor.autoPrecision = true;
|
||||
numericDescriptor.precision = 5;
|
||||
|
||||
const actualValue: string = getFormattedValueWithFallback(
|
||||
value,
|
||||
numericDescriptor,
|
||||
);
|
||||
|
||||
expect(actualValue).toBe(expectedValue);
|
||||
});
|
||||
|
||||
it("should return 1.23M for 1234035 if precision is auto and display units are auto", () => {
|
||||
const value: number = 1234035;
|
||||
const expectedValue: string = "1.23M";
|
||||
const numericDescriptor: NumericDescriptor = new NumericDescriptor();
|
||||
numericDescriptor.autoPrecision = true;
|
||||
numericDescriptor.precision = 5;
|
||||
|
||||
const actualValue: string = getFormattedValueWithFallback(
|
||||
value,
|
||||
numericDescriptor,
|
||||
);
|
||||
|
||||
expect(actualValue).toBe(expectedValue);
|
||||
});
|
||||
|
||||
it("should return 1234K for 1234035 if precision is auto and display units are thousands", () => {
|
||||
const value: number = 1234035;
|
||||
const expectedValue: string = "1234K";
|
||||
const numericDescriptor: NumericDescriptor = new NumericDescriptor();
|
||||
numericDescriptor.autoPrecision = true;
|
||||
numericDescriptor.displayUnits = 1000;
|
||||
numericDescriptor.precision = 5;
|
||||
|
||||
const actualValue: string = getFormattedValueWithFallback(
|
||||
value,
|
||||
numericDescriptor,
|
||||
);
|
||||
|
||||
expect(actualValue).toBe(expectedValue);
|
||||
});
|
||||
|
||||
it("should return 1.23K% for 12.34312 if precision is auto and display units are thousand and default format in percents", () => {
|
||||
const value: number = 12.34312;
|
||||
const expectedValue: string = "+1.23K%";
|
||||
const numericDescriptor: NumericDescriptor = new NumericDescriptor();
|
||||
numericDescriptor.autoPrecision = true;
|
||||
numericDescriptor.displayUnits = 1000;
|
||||
numericDescriptor.defaultFormat = "+0.00%;-0.00%;0.00%";
|
||||
numericDescriptor.precision = 5;
|
||||
|
||||
const actualValue: string = getFormattedValueWithFallback(
|
||||
value,
|
||||
numericDescriptor,
|
||||
);
|
||||
|
||||
expect(actualValue).toBe(expectedValue);
|
||||
});
|
||||
|
||||
it("should return 100% for 1.012 if precision is auto and display units are auto and format in percents", () => {
|
||||
const value: number = 1.0012;
|
||||
const expectedValue: string = "+100%";
|
||||
const numericDescriptor: NumericDescriptor = new NumericDescriptor();
|
||||
numericDescriptor.autoPrecision = true;
|
||||
numericDescriptor.format = "+0.00%;-0.00%;0.00%";
|
||||
numericDescriptor.precision = 5;
|
||||
|
||||
const actualValue: string = getFormattedValueWithFallback(
|
||||
value,
|
||||
numericDescriptor,
|
||||
);
|
||||
|
||||
expect(actualValue).toBe(expectedValue);
|
||||
});
|
||||
|
||||
it("should return 87.7% for 0.87712 if precision is auto and display units are auto and default format in percents", () => {
|
||||
const value: number = 0.87712;
|
||||
const expectedValue: string = "+87.7%";
|
||||
const numericDescriptor: NumericDescriptor = new NumericDescriptor();
|
||||
numericDescriptor.autoPrecision = true;
|
||||
numericDescriptor.defaultFormat = "+0.00%;-0.00%;0.00%";
|
||||
numericDescriptor.precision = 5;
|
||||
|
||||
const actualValue: string = getFormattedValueWithFallback(
|
||||
value,
|
||||
numericDescriptor,
|
||||
);
|
||||
|
||||
expect(actualValue).toBe(expectedValue);
|
||||
});
|
||||
|
||||
it("should return 7.43% for 0.0742712 if precision is auto and display units are auto and column format in percents", () => {
|
||||
const value: number = 0.0742712;
|
||||
const expectedValue: string = "+7.43%";
|
||||
const numericDescriptor: NumericDescriptor = new NumericDescriptor();
|
||||
numericDescriptor.autoPrecision = true;
|
||||
numericDescriptor.columnFormat = "+0.00%;-0.00%;0.00%";
|
||||
numericDescriptor.precision = 5;
|
||||
|
||||
const actualValue: string = getFormattedValueWithFallback(
|
||||
value,
|
||||
numericDescriptor,
|
||||
);
|
||||
|
||||
expect(actualValue).toBe(expectedValue);
|
||||
});
|
||||
|
||||
it("should return -0.02% for -0.00293312 if precision is auto and display units are auto and column format in percents", () => {
|
||||
const value: number = -0.000243312;
|
||||
const expectedValue: string = "-0.02%";
|
||||
const numericDescriptor: NumericDescriptor = new NumericDescriptor();
|
||||
numericDescriptor.autoPrecision = true;
|
||||
numericDescriptor.columnFormat = "+0.00%;-0.00%;0.00%";
|
||||
numericDescriptor.precision = 5;
|
||||
|
||||
const actualValue: string = getFormattedValueWithFallback(
|
||||
value,
|
||||
numericDescriptor,
|
||||
);
|
||||
|
||||
expect(actualValue).toBe(expectedValue);
|
||||
});
|
||||
|
||||
it("should return 1354% for 1.3537905 if precision is auto and display units are auto and format in percents", () => {
|
||||
const value: number = 13.537905;
|
||||
const expectedValue: string = "+1354%";
|
||||
const numericDescriptor: NumericDescriptor = new NumericDescriptor();
|
||||
numericDescriptor.autoPrecision = true;
|
||||
numericDescriptor.format = "+0.00%;-0.00%;0.00%";
|
||||
numericDescriptor.precision = 5;
|
||||
|
||||
const actualValue: string = getFormattedValueWithFallback(
|
||||
value,
|
||||
numericDescriptor,
|
||||
);
|
||||
|
||||
expect(actualValue).toBe(expectedValue);
|
||||
});
|
||||
});
|
||||
|
||||
it("stale data is set up for a metric", (done) => {
|
||||
const testWrapper: TestWrapper = TestWrapper.CREATE(true, 0, 1);
|
||||
castZeroToNullOrReturnBack(testWrapper.dataView);
|
||||
|
||||
testWrapper.dataView.metadata.objects = {
|
||||
staleData: {
|
||||
show: true,
|
||||
staleDataText: "label {$1}",
|
||||
staleDataThreshold: 1,
|
||||
},
|
||||
subtitle: {
|
||||
show: true,
|
||||
},
|
||||
values: {
|
||||
showLatterAvailableValue: true,
|
||||
treatEmptyValuesAsZero: false,
|
||||
},
|
||||
};
|
||||
|
||||
// This call will be skipped because of isShown = false
|
||||
testWrapper.dataView.metadata.columns[2].objects = {
|
||||
staleData: {
|
||||
isShown: false,
|
||||
},
|
||||
};
|
||||
// This call will be skipped because of threshold days
|
||||
testWrapper.dataView.metadata.columns[3].objects = {
|
||||
staleData: {
|
||||
staleDataText: "unique {$1}",
|
||||
staleDataThreshold: 5,
|
||||
},
|
||||
};
|
||||
testWrapper.dataView.metadata.columns[4].objects = {
|
||||
staleData: {
|
||||
staleDataText: "custom label {$1}",
|
||||
staleDataThreshold: 0,
|
||||
},
|
||||
};
|
||||
|
||||
const components = testWrapper.visualBuilder.instance.rootComponent.getComponents();
|
||||
const warningComponent = <SubtitleWarningComponent>(components.filter(c => c instanceof SubtitleWarningComponent)[0]);
|
||||
spyOn(warningComponent, "getTitle");
|
||||
|
||||
testWrapper.visualBuilder.updateRenderTimeout(testWrapper.dataView, () => {
|
||||
expect(warningComponent.getTitle).toHaveBeenCalledWith("label {$1}", 4, 0);
|
||||
expect(warningComponent.getTitle).toHaveBeenCalledWith("custom label {$1}", 1, 0);
|
||||
expect(warningComponent.getTitle).toHaveBeenCalledTimes(2);
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it("stale data is set up for a metric with staleDataText from susbtitles (compatibility support)", (done) => {
|
||||
const testWrapper: TestWrapper = TestWrapper.CREATE(true, 0, 2);
|
||||
castZeroToNullOrReturnBack(testWrapper.dataView);
|
||||
|
||||
testWrapper.dataView.metadata.objects = {
|
||||
staleData: {
|
||||
show: true,
|
||||
},
|
||||
subtitle: {
|
||||
show: true,
|
||||
staleDataText: "Compatibility title"
|
||||
},
|
||||
values: {
|
||||
showLatterAvailableValue: true,
|
||||
treatEmptyValuesAsZero: false,
|
||||
},
|
||||
};
|
||||
|
||||
testWrapper.dataView.metadata.columns[2].objects = {
|
||||
staleData: {
|
||||
isShown: false,
|
||||
},
|
||||
};
|
||||
testWrapper.dataView.metadata.columns[3].objects = {
|
||||
staleData: {
|
||||
staleDataText: "unique {$1}",
|
||||
staleDataThreshold: 1,
|
||||
},
|
||||
};
|
||||
testWrapper.dataView.metadata.columns[4].objects = {
|
||||
staleData: {
|
||||
staleDataText: "custom label {$1}",
|
||||
staleDataThreshold: 1,
|
||||
},
|
||||
};
|
||||
|
||||
const components = testWrapper.visualBuilder.instance.rootComponent.getComponents();
|
||||
const warningComponent = <SubtitleWarningComponent>(components.filter(c => c instanceof SubtitleWarningComponent)[0]);
|
||||
spyOn(warningComponent, "getTitle");
|
||||
|
||||
testWrapper.visualBuilder.updateRenderTimeout(testWrapper.dataView, () => {
|
||||
expect(warningComponent.getTitle).toHaveBeenCalledWith("Data is ${1} days late.Compatibility title", 5, 0);
|
||||
expect(warningComponent.getTitle).toHaveBeenCalledWith("unique {$1}", 2, 0);
|
||||
expect(warningComponent.getTitle).toHaveBeenCalledWith("custom label {$1}", 2, 0);
|
||||
expect(warningComponent.getTitle).toHaveBeenCalledTimes(3);
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it("stale data with goup threshold days deduction option", (done) => {
|
||||
const testWrapper: TestWrapper = TestWrapper.CREATE(true, 0, 1);
|
||||
castZeroToNullOrReturnBack(testWrapper.dataView);
|
||||
|
||||
testWrapper.dataView.metadata.objects = {
|
||||
staleData: {
|
||||
show: true,
|
||||
staleDataThreshold: 0,
|
||||
deductThresholdDays: true,
|
||||
},
|
||||
subtitle: {
|
||||
show: true,
|
||||
},
|
||||
values: {
|
||||
showLatterAvailableValue: true,
|
||||
treatEmptyValuesAsZero: false,
|
||||
},
|
||||
};
|
||||
|
||||
testWrapper.dataView.metadata.columns[1].objects = {
|
||||
staleData: {
|
||||
staleDataText: "first label {$1}",
|
||||
staleDataThreshold: 2,
|
||||
},
|
||||
};
|
||||
testWrapper.dataView.metadata.columns[2].objects = {
|
||||
staleData: {
|
||||
staleDataText: "second label {$1}",
|
||||
},
|
||||
};
|
||||
|
||||
// because of high threshold days limit, this item is actual and will not be shown in stale data block
|
||||
testWrapper.dataView.metadata.columns[3].objects = {
|
||||
staleData: {
|
||||
staleDataText: "third label {$1}",
|
||||
staleDataThreshold: 33,
|
||||
},
|
||||
};
|
||||
|
||||
const components = testWrapper.visualBuilder.instance.rootComponent.getComponents();
|
||||
const warningComponent = <SubtitleWarningComponent>(components.filter(c => c instanceof SubtitleWarningComponent)[0]);
|
||||
spyOn(warningComponent, "getTitle");
|
||||
|
||||
testWrapper.visualBuilder.updateRenderTimeout(testWrapper.dataView, () => {
|
||||
expect(warningComponent.getTitle).toHaveBeenCalledWith("first label {$1}", 4, 2);
|
||||
expect(warningComponent.getTitle).toHaveBeenCalledWith("second label {$1}", 1, 0);
|
||||
expect(warningComponent.getTitle).toHaveBeenCalledWith("Data is ${1} days late.", 1, 0);
|
||||
expect(warningComponent.getTitle).toHaveBeenCalledTimes(3);
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it("stale data with threshold deduction per metric", (done) => {
|
||||
const testWrapper: TestWrapper = TestWrapper.CREATE(true, 0, 5);
|
||||
castZeroToNullOrReturnBack(testWrapper.dataView);
|
||||
|
||||
testWrapper.dataView.metadata.objects = {
|
||||
staleData: {
|
||||
show: true,
|
||||
staleDataThreshold: 1,
|
||||
deductThresholdDays: false,
|
||||
},
|
||||
subtitle: {
|
||||
show: true,
|
||||
},
|
||||
values: {
|
||||
showLatterAvailableValue: true,
|
||||
treatEmptyValuesAsZero: false,
|
||||
},
|
||||
};
|
||||
|
||||
testWrapper.dataView.metadata.columns[1].objects = {
|
||||
staleData: {
|
||||
staleDataText: "first label {$1}",
|
||||
staleDataThreshold: 2,
|
||||
},
|
||||
};
|
||||
testWrapper.dataView.metadata.columns[2].objects = {
|
||||
staleData: {
|
||||
staleDataText: "second label {$1}",
|
||||
staleDataThreshold: 3,
|
||||
deductThresholdDays: true,
|
||||
},
|
||||
};
|
||||
|
||||
// because of high threshold days limit, this item is actual and will not be shown in stale data block
|
||||
testWrapper.dataView.metadata.columns[3].objects = {
|
||||
staleData: {
|
||||
staleDataText: "third label {$1}",
|
||||
staleDataThreshold: 33,
|
||||
},
|
||||
};
|
||||
|
||||
const components = testWrapper.visualBuilder.instance.rootComponent.getComponents();
|
||||
const warningComponent = <SubtitleWarningComponent>(components.filter(c => c instanceof SubtitleWarningComponent)[0]);
|
||||
spyOn(warningComponent, "getTitle");
|
||||
|
||||
testWrapper.visualBuilder.updateRenderTimeout(testWrapper.dataView, () => {
|
||||
expect(warningComponent.getTitle).toHaveBeenCalledWith("first label {$1}", 8, 0);
|
||||
expect(warningComponent.getTitle).toHaveBeenCalledWith("second label {$1}", 5, 3);
|
||||
expect(warningComponent.getTitle).toHaveBeenCalledWith("Data is ${1} days late.", 5, 0);
|
||||
expect(warningComponent.getTitle).toHaveBeenCalledTimes(3);
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it("subtitle shouldn't be rendered if it's turned off in Format Panel explicitly", (done) => {
|
||||
const testWrapper: TestWrapper = TestWrapper.CREATE();
|
||||
|
||||
testWrapper.dataView.metadata.objects = {
|
||||
subtitle: {
|
||||
show: false,
|
||||
titleText: "Power BI rocks",
|
||||
},
|
||||
};
|
||||
|
||||
testWrapper.visualBuilder.updateRenderTimeout(testWrapper.dataView, () => {
|
||||
const displayStatus = testWrapper.visualBuilder.$subtitle.css("display");
|
||||
expect(displayStatus).toEqual("none");
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it("merged subtitle should be rendered if it's turned on in Format Panel and provided as data field", (done) => {
|
||||
const testWrapper: TestWrapper = TestWrapper
|
||||
.CREATE(undefined, undefined, undefined, true);
|
||||
|
||||
testWrapper.dataView.metadata.objects = {
|
||||
subtitle: {
|
||||
show: true,
|
||||
titleText: "Power BI rocks",
|
||||
},
|
||||
};
|
||||
|
||||
testWrapper.visualBuilder.updateRenderTimeout(testWrapper.dataView, () => {
|
||||
const displayStatus = testWrapper.visualBuilder.$subtitle.css("display");
|
||||
expect(displayStatus).toEqual("flex");
|
||||
const subtitleElm = testWrapper.visualBuilder.$subtitle.find(".multiKpi_subtitle");
|
||||
expect(subtitleElm.text()).toEqual("Power BI rocksSubtitle form data");
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it("subtitle from data should be rendered if it's turned on in Format Panel and provided only as data field", (done) => {
|
||||
const testWrapper: TestWrapper = TestWrapper
|
||||
.CREATE(undefined, undefined, undefined, true);
|
||||
|
||||
testWrapper.dataView.metadata.objects = {
|
||||
subtitle: {
|
||||
show: true,
|
||||
},
|
||||
};
|
||||
|
||||
testWrapper.visualBuilder.updateRenderTimeout(testWrapper.dataView, () => {
|
||||
const displayStatus = testWrapper.visualBuilder.$subtitle.css("display");
|
||||
expect(displayStatus).toEqual("flex");
|
||||
const subtitleElm = testWrapper.visualBuilder.$subtitle.find(".multiKpi_subtitle");
|
||||
expect(subtitleElm.text()).toEqual("Subtitle form data");
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe("Version 2.2.0 Changes", () => {
|
||||
it("Treat Empty/Missing Values As Zero is enabled", (done) => {
|
||||
const testWrapper: TestWrapper = TestWrapper.create(true, 2);
|
||||
const testWrapper: TestWrapper = TestWrapper.CREATE(true, 2);
|
||||
|
||||
castZeroToNullOrReturnBack(testWrapper.dataView);
|
||||
|
||||
|
@ -86,7 +532,7 @@ describe("Multi KPI", () => {
|
|||
});
|
||||
|
||||
it("Treat Empty/Missing Values As Zero is disabled", (done) => {
|
||||
const testWrapper: TestWrapper = TestWrapper.create(true, 2);
|
||||
const testWrapper: TestWrapper = TestWrapper.CREATE(true, 2);
|
||||
|
||||
castZeroToNullOrReturnBack(testWrapper.dataView);
|
||||
|
||||
|
@ -104,7 +550,7 @@ describe("Multi KPI", () => {
|
|||
});
|
||||
|
||||
it("Treat Empty/Missing Values As Zero is disabled but Show Latest Available As Current Value is enabled", (done) => {
|
||||
const testWrapper: TestWrapper = TestWrapper.create(true, 2);
|
||||
const testWrapper: TestWrapper = TestWrapper.CREATE(true, 2);
|
||||
|
||||
castZeroToNullOrReturnBack(testWrapper.dataView);
|
||||
|
||||
|
@ -123,7 +569,7 @@ describe("Multi KPI", () => {
|
|||
});
|
||||
|
||||
it("Treat Empty/Missing Values As Zero is enabled and Show Latest Available As Current Value is enabled", (done) => {
|
||||
const testWrapper: TestWrapper = TestWrapper.create(true, 2);
|
||||
const testWrapper: TestWrapper = TestWrapper.CREATE(true, 2);
|
||||
|
||||
castZeroToNullOrReturnBack(testWrapper.dataView);
|
||||
|
||||
|
@ -142,7 +588,7 @@ describe("Multi KPI", () => {
|
|||
});
|
||||
|
||||
it("Missing Value label is customized", (done) => {
|
||||
const testWrapper: TestWrapper = TestWrapper.create(true, 2);
|
||||
const testWrapper: TestWrapper = TestWrapper.CREATE(true, 2);
|
||||
|
||||
castZeroToNullOrReturnBack(testWrapper.dataView);
|
||||
|
||||
|
@ -163,7 +609,7 @@ describe("Multi KPI", () => {
|
|||
});
|
||||
|
||||
it("Missing Value label is customized generally and for certain series", (done) => {
|
||||
const testWrapper: TestWrapper = TestWrapper.create(true, 2);
|
||||
const testWrapper: TestWrapper = TestWrapper.CREATE(true, 2);
|
||||
|
||||
castZeroToNullOrReturnBack(testWrapper.dataView);
|
||||
|
||||
|
@ -190,7 +636,7 @@ describe("Multi KPI", () => {
|
|||
});
|
||||
|
||||
it("Missing Variance label is customized", (done) => {
|
||||
const testWrapper: TestWrapper = TestWrapper.create(true, 0);
|
||||
const testWrapper: TestWrapper = TestWrapper.CREATE(true, 0);
|
||||
|
||||
castZeroToNullOrReturnBack(testWrapper.dataView);
|
||||
|
||||
|
@ -211,7 +657,7 @@ describe("Multi KPI", () => {
|
|||
});
|
||||
|
||||
it("Missing Variance label is customized generally and for certain series", (done) => {
|
||||
const testWrapper: TestWrapper = TestWrapper.create(true, 0);
|
||||
const testWrapper: TestWrapper = TestWrapper.CREATE(true, 0);
|
||||
|
||||
castZeroToNullOrReturnBack(testWrapper.dataView);
|
||||
|
||||
|
@ -238,7 +684,7 @@ describe("Multi KPI", () => {
|
|||
});
|
||||
|
||||
it("Stale Data is enabled but Dates are actual", (done) => {
|
||||
const testWrapper: TestWrapper = TestWrapper.create(true, 0);
|
||||
const testWrapper: TestWrapper = TestWrapper.CREATE(true, 0);
|
||||
|
||||
castZeroToNullOrReturnBack(testWrapper.dataView);
|
||||
|
||||
|
@ -264,7 +710,7 @@ describe("Multi KPI", () => {
|
|||
});
|
||||
|
||||
it("Stale Data is enabled and be shown", (done) => {
|
||||
const testWrapper: TestWrapper = TestWrapper.create(true, 0, 1);
|
||||
const testWrapper: TestWrapper = TestWrapper.CREATE(true, 0, 1);
|
||||
|
||||
castZeroToNullOrReturnBack(testWrapper.dataView);
|
||||
|
||||
|
@ -287,7 +733,7 @@ describe("Multi KPI", () => {
|
|||
});
|
||||
|
||||
it("Stale Data is enabled but Threshold Days are actual", (done) => {
|
||||
const testWrapper: TestWrapper = TestWrapper.create(true, 0, 1);
|
||||
const testWrapper: TestWrapper = TestWrapper.CREATE(true, 0, 1);
|
||||
|
||||
castZeroToNullOrReturnBack(testWrapper.dataView);
|
||||
|
||||
|
@ -310,7 +756,7 @@ describe("Multi KPI", () => {
|
|||
});
|
||||
|
||||
it("Stale Data is enabled but One of the metrics have more obsolete data than others", (done) => {
|
||||
const testWrapper: TestWrapper = TestWrapper.create(true, 0, 1);
|
||||
const testWrapper: TestWrapper = TestWrapper.CREATE(true, 0, 1);
|
||||
|
||||
castZeroToNullOrReturnBack(testWrapper.dataView);
|
||||
|
||||
|
@ -338,7 +784,7 @@ describe("Multi KPI", () => {
|
|||
});
|
||||
|
||||
it("Stale Data is enabled and has sufficient threshold days to handle any metrics, even if one of them more obsolete", (done) => {
|
||||
const testWrapper: TestWrapper = TestWrapper.create(true, 0, 1);
|
||||
const testWrapper: TestWrapper = TestWrapper.CREATE(true, 0, 1);
|
||||
|
||||
castZeroToNullOrReturnBack(testWrapper.dataView);
|
||||
|
||||
|
@ -368,7 +814,7 @@ describe("Multi KPI", () => {
|
|||
|
||||
describe("DOM", () => {
|
||||
it("root element should be defined in DOM", (done) => {
|
||||
const testWrapper: TestWrapper = TestWrapper.create();
|
||||
const testWrapper: TestWrapper = TestWrapper.CREATE();
|
||||
|
||||
testWrapper.visualBuilder.updateRenderTimeout(testWrapper.dataView, () => {
|
||||
expect(testWrapper.visualBuilder.$root).toBeInDOM();
|
||||
|
@ -379,7 +825,7 @@ describe("Multi KPI", () => {
|
|||
|
||||
describe("Main Chart", () => {
|
||||
it("the main chart should be rendered", (done) => {
|
||||
const testWrapper: TestWrapper = TestWrapper.create();
|
||||
const testWrapper: TestWrapper = TestWrapper.CREATE();
|
||||
|
||||
testWrapper.visualBuilder.updateRenderTimeout(testWrapper.dataView, () => {
|
||||
expect(testWrapper.visualBuilder.$mainChart).toBeInDOM();
|
||||
|
@ -410,6 +856,7 @@ describe("Multi KPI", () => {
|
|||
alternativeColor: "blue",
|
||||
color: "green",
|
||||
current: undefined,
|
||||
isLine: false,
|
||||
points: [
|
||||
{
|
||||
index: 0,
|
||||
|
@ -449,7 +896,7 @@ describe("Multi KPI", () => {
|
|||
max: maxDate,
|
||||
min: minDate,
|
||||
scale: DataRepresentationScale
|
||||
.create()
|
||||
.CREATE()
|
||||
.domain(
|
||||
[minDate, maxDate],
|
||||
DataRepresentationTypeEnum.DateType,
|
||||
|
@ -461,7 +908,7 @@ describe("Multi KPI", () => {
|
|||
max: maxValue,
|
||||
min: minValue,
|
||||
scale: DataRepresentationScale
|
||||
.create()
|
||||
.CREATE()
|
||||
.domain(
|
||||
[minValue, maxValue],
|
||||
DataRepresentationTypeEnum.NumberType,
|
||||
|
@ -483,7 +930,7 @@ describe("Multi KPI", () => {
|
|||
|
||||
describe("Sparkline", () => {
|
||||
it("sparkline component should be rendered", (done) => {
|
||||
const testWrapper: TestWrapper = TestWrapper.create();
|
||||
const testWrapper: TestWrapper = TestWrapper.CREATE();
|
||||
|
||||
testWrapper.visualBuilder.updateRenderTimeout(testWrapper.dataView, () => {
|
||||
testWrapper.visualBuilder.$sparkline.each(function checkEachElement() {
|
||||
|
@ -498,7 +945,7 @@ describe("Multi KPI", () => {
|
|||
|
||||
describe("Subtitle", () => {
|
||||
it("subtitle of each sparkline should be rendered", (done) => {
|
||||
const testWrapper: TestWrapper = TestWrapper.create();
|
||||
const testWrapper: TestWrapper = TestWrapper.CREATE();
|
||||
|
||||
testWrapper.visualBuilder.updateRenderTimeout(testWrapper.dataView, () => {
|
||||
testWrapper.visualBuilder.$sparklineSubtitle.each(function checkEachElement() {
|
||||
|
@ -514,7 +961,7 @@ describe("Multi KPI", () => {
|
|||
|
||||
describe("Line", () => {
|
||||
it("line of each sparkline should be rendered", (done) => {
|
||||
const testWrapper: TestWrapper = TestWrapper.create();
|
||||
const testWrapper: TestWrapper = TestWrapper.CREATE();
|
||||
|
||||
testWrapper.visualBuilder.updateRenderTimeout(testWrapper.dataView, () => {
|
||||
testWrapper.visualBuilder.$sparklineLine.each(function checkEachElement() {
|
||||
|
@ -531,7 +978,7 @@ describe("Multi KPI", () => {
|
|||
|
||||
describe("Subtitle", () => {
|
||||
it("subtitle should be rendered if it's turned on via Format Panel explicitly", (done) => {
|
||||
const testWrapper: TestWrapper = TestWrapper.create();
|
||||
const testWrapper: TestWrapper = TestWrapper.CREATE();
|
||||
|
||||
testWrapper.dataView.metadata.objects = {
|
||||
subtitle: {
|
||||
|
@ -541,8 +988,10 @@ describe("Multi KPI", () => {
|
|||
};
|
||||
|
||||
testWrapper.visualBuilder.updateRenderTimeout(testWrapper.dataView, () => {
|
||||
expect(testWrapper.visualBuilder.$subtitle).toBeInDOM();
|
||||
|
||||
const displayStatus = testWrapper.visualBuilder.$subtitle.css("display");
|
||||
expect(displayStatus).toEqual("flex");
|
||||
const subtitleElm = testWrapper.visualBuilder.$subtitle.find(".multiKpi_subtitle");
|
||||
expect(subtitleElm.text()).toEqual("Power BI rocks");
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
@ -696,14 +1145,14 @@ describe("Multi KPI", () => {
|
|||
});
|
||||
});
|
||||
|
||||
function createElement(viewport: powerbi.IViewport = { height: 600, width: 800 }): Selection<any, any, any, any> {
|
||||
return d3Select(testDom(
|
||||
function createElement(viewport: powerbiVisualsApi.IViewport = { height: 600, width: 800 }): Selection<any, any, any, any> {
|
||||
return d3Select($(testDom(
|
||||
viewport.height.toString(),
|
||||
viewport.width.toString(),
|
||||
).get(0));
|
||||
)).get(0));
|
||||
}
|
||||
|
||||
function castZeroToNullOrReturnBack(dataView: powerbi.DataView): void {
|
||||
function castZeroToNullOrReturnBack(dataView: powerbiVisualsApi.DataView): void {
|
||||
dataView.categorical.values.forEach((x) => x.values.forEach((value, index, theArray) => {
|
||||
if (value === 0) {
|
||||
theArray[index] = null;
|
||||
|
|
|
@ -26,12 +26,14 @@
|
|||
|
||||
import { VisualBuilderBase } from "powerbi-visuals-utils-testutils";
|
||||
|
||||
import { MultiKpi } from "../src/visual";
|
||||
import { MultiKpi } from "../src/multiKpi";
|
||||
|
||||
import * as $ from "jquery";
|
||||
|
||||
export class MultiKpiBuilder extends VisualBuilderBase<MultiKpi> {
|
||||
protected build(): MultiKpi {
|
||||
return new MultiKpi({
|
||||
element: this.element.get(0),
|
||||
element: $(this.element).get(0),
|
||||
host: this.visualHost,
|
||||
});
|
||||
}
|
||||
|
@ -41,7 +43,7 @@ export class MultiKpiBuilder extends VisualBuilderBase<MultiKpi> {
|
|||
}
|
||||
|
||||
public get $root(): JQuery {
|
||||
return this.element.find(".multiKpi_multiKpi");
|
||||
return $(this.element).find(".multiKpi_multiKpi");
|
||||
}
|
||||
|
||||
public get $sparkline(): JQuery {
|
|
@ -24,7 +24,7 @@
|
|||
* THE SOFTWARE.
|
||||
*/
|
||||
|
||||
import powerbi from "powerbi-visuals-api";
|
||||
import powerbiVisualsApi from "powerbi-visuals-api";
|
||||
|
||||
import { getRandomNumbers, testDataViewBuilder } from "powerbi-visuals-utils-testutils";
|
||||
import { valueType } from "powerbi-visuals-utils-typeutils";
|
||||
|
@ -32,6 +32,7 @@ import { valueType } from "powerbi-visuals-utils-typeutils";
|
|||
import {
|
||||
dateColumn,
|
||||
valueColumn,
|
||||
subtitleColumn,
|
||||
} from "../src/columns/columns";
|
||||
|
||||
export class MultiKpiData extends testDataViewBuilder.TestDataViewBuilder {
|
||||
|
@ -40,7 +41,10 @@ export class MultiKpiData extends testDataViewBuilder.TestDataViewBuilder {
|
|||
public dates: Date[] = [];
|
||||
public seriesValues: number[][] = [];
|
||||
|
||||
constructor(withMisisngValues?: boolean, brokenMetricIndex: number = 0, howOlderDatesAreInDays: number = 0) {
|
||||
constructor(
|
||||
withMisisngValues: boolean = false,
|
||||
brokenMetricIndex: number = 0,
|
||||
howOlderDatesAreInDays: number = 0) {
|
||||
super();
|
||||
|
||||
const today = new Date();
|
||||
|
@ -96,18 +100,57 @@ export class MultiKpiData extends testDataViewBuilder.TestDataViewBuilder {
|
|||
}
|
||||
}
|
||||
|
||||
public getDataView(columnNames?: string[]): powerbi.DataView {
|
||||
const datesCategory = {
|
||||
public getDataView(columnNames?: string[]): powerbiVisualsApi.DataView {
|
||||
const datesCategory = this.buildDatesCategory(this.dates);
|
||||
const valuesCategory = this.buildValuesCategory(this.seriesValues);
|
||||
|
||||
return this.createCategoricalDataViewBuilder(
|
||||
[datesCategory],
|
||||
valuesCategory,
|
||||
columnNames,
|
||||
).build();
|
||||
}
|
||||
|
||||
public getDataViewWithSubtitle(columnNames?: string[]): powerbiVisualsApi.DataView {
|
||||
const datesCategory = this.buildDatesCategory(this.dates);
|
||||
const valuesCategory = this.buildValuesCategory(this.seriesValues);
|
||||
const repeatsNum: number = this.dates.length;
|
||||
let subtitleArr: string[] = [];
|
||||
for (let i = 0; i < repeatsNum; i++) {
|
||||
subtitleArr.push("Subtitle form data");
|
||||
}
|
||||
|
||||
valuesCategory.push({
|
||||
source: {
|
||||
displayName: subtitleColumn.name,
|
||||
roles: { subtitleColumn: true },
|
||||
type: valueType.ValueType.fromDescriptor({ text: true }),
|
||||
},
|
||||
values: subtitleArr,
|
||||
});
|
||||
|
||||
return this.createCategoricalDataViewBuilder(
|
||||
[datesCategory],
|
||||
valuesCategory,
|
||||
columnNames,
|
||||
).build();
|
||||
}
|
||||
|
||||
|
||||
private buildDatesCategory(dates: Date[]): any {
|
||||
return {
|
||||
source: {
|
||||
displayName: dateColumn.name,
|
||||
format: "%M/%d/yyyy",
|
||||
roles: { dateColumn: true },
|
||||
type: valueType.ValueType.fromDescriptor({ dateTime: true }),
|
||||
},
|
||||
values: this.dates,
|
||||
};
|
||||
values: dates,
|
||||
}
|
||||
}
|
||||
|
||||
const valuesCategory = this.seriesValues
|
||||
private buildValuesCategory(seriesValues: number[][]): any {
|
||||
return seriesValues
|
||||
.map((values: number[]) => {
|
||||
return {
|
||||
source: {
|
||||
|
@ -118,11 +161,5 @@ export class MultiKpiData extends testDataViewBuilder.TestDataViewBuilder {
|
|||
values,
|
||||
};
|
||||
});
|
||||
|
||||
return this.createCategoricalDataViewBuilder(
|
||||
[datesCategory],
|
||||
valuesCategory,
|
||||
columnNames,
|
||||
).build();
|
||||
}
|
||||
}
|
|
@ -24,20 +24,25 @@
|
|||
* THE SOFTWARE.
|
||||
*/
|
||||
|
||||
import powerbi from "powerbi-visuals-api";
|
||||
import powerbiVisualsApi from "powerbi-visuals-api";
|
||||
|
||||
import { MultiKpiData } from "./dataBuilder";
|
||||
import { MultiKpiBuilder } from "./visualBuilder";
|
||||
import { MultiKpiData } from "./multiKpiData";
|
||||
import { MultiKpiBuilder } from "./multiKpiBuilder";
|
||||
|
||||
export class TestWrapper {
|
||||
public static create(
|
||||
public static CREATE(
|
||||
withMisisngValues: boolean = false,
|
||||
brokenMetricIndex: number = 0,
|
||||
howOlderDatesAreInDays: number = 0): TestWrapper {
|
||||
return new TestWrapper(withMisisngValues, brokenMetricIndex, howOlderDatesAreInDays);
|
||||
howOlderDatesAreInDays: number = 0,
|
||||
attachSubtitleData: boolean = false): TestWrapper {
|
||||
return new TestWrapper(
|
||||
withMisisngValues,
|
||||
brokenMetricIndex,
|
||||
howOlderDatesAreInDays,
|
||||
attachSubtitleData);
|
||||
}
|
||||
|
||||
public dataView: powerbi.DataView;
|
||||
public dataView: powerbiVisualsApi.DataView;
|
||||
public dataViewBuilder: MultiKpiData;
|
||||
public visualBuilder: MultiKpiBuilder;
|
||||
|
||||
|
@ -45,12 +50,20 @@ export class TestWrapper {
|
|||
withMisisngValues: boolean = false,
|
||||
brokenMetricIndex: number = 0,
|
||||
howOlderDatesAreInDays: number = 0,
|
||||
attachSubtitleData: boolean = false,
|
||||
width: number = 1024,
|
||||
height: number = 768) {
|
||||
|
||||
this.visualBuilder = new MultiKpiBuilder(width, height);
|
||||
this.dataViewBuilder = new MultiKpiData(withMisisngValues, brokenMetricIndex, howOlderDatesAreInDays);
|
||||
this.dataViewBuilder = new MultiKpiData(
|
||||
withMisisngValues,
|
||||
brokenMetricIndex,
|
||||
howOlderDatesAreInDays);
|
||||
|
||||
this.dataView = this.dataViewBuilder.getDataView();
|
||||
if (attachSubtitleData) {
|
||||
this.dataView = this.dataViewBuilder.getDataViewWithSubtitle();
|
||||
} else {
|
||||
this.dataView = this.dataViewBuilder.getDataView();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -53,3 +53,8 @@ export const changeStartDateColumn: IVisualDataRole = {
|
|||
displayName: "Change start date",
|
||||
name: "changeStartDateColumn",
|
||||
};
|
||||
|
||||
export const subtitleColumn: IVisualDataRole = {
|
||||
displayName: "Subtitle",
|
||||
name: "subtitleColumn",
|
||||
};
|
||||
|
|
|
@ -24,7 +24,7 @@
|
|||
* THE SOFTWARE.
|
||||
*/
|
||||
|
||||
import powerbi from "powerbi-visuals-api";
|
||||
import powerbiVisualsApi from "powerbi-visuals-api";
|
||||
|
||||
import {
|
||||
changeStartDateColumn,
|
||||
|
@ -32,6 +32,7 @@ import {
|
|||
tooltipColumn,
|
||||
valueColumn,
|
||||
warningStateColumn,
|
||||
subtitleColumn,
|
||||
} from "../../columns/columns";
|
||||
|
||||
import { AxisDescriptor } from "../../settings/descriptors/axisDescriptor";
|
||||
|
@ -55,7 +56,7 @@ import {
|
|||
DataRepresentationTypeEnum,
|
||||
} from "../data/dataRepresentationScale";
|
||||
|
||||
import { createVarianceConverterByType } from "../variance";
|
||||
import { createVarianceConverterByType } from "../variance/createVarianceConverterByType";
|
||||
|
||||
import {
|
||||
getFormattedDate,
|
||||
|
@ -65,7 +66,7 @@ import {
|
|||
export interface IColumnGroup {
|
||||
name: string;
|
||||
values: any[];
|
||||
columns: Array<powerbi.DataViewValueColumn | powerbi.DataViewCategoryColumn>;
|
||||
columns: (powerbiVisualsApi.DataViewValueColumn | powerbiVisualsApi.DataViewCategoryColumn)[];
|
||||
}
|
||||
|
||||
export interface IColumnGroupByRole {
|
||||
|
@ -73,13 +74,13 @@ export interface IColumnGroupByRole {
|
|||
}
|
||||
|
||||
export interface IDataConverterConstructorOptions {
|
||||
createSelectionIdBuilder: () => powerbi.visuals.ISelectionIdBuilder;
|
||||
createSelectionIdBuilder: () => powerbiVisualsApi.visuals.ISelectionIdBuilder;
|
||||
}
|
||||
|
||||
export interface IDataConverterOptions {
|
||||
dataView: powerbi.DataView;
|
||||
dataView: powerbiVisualsApi.DataView;
|
||||
settings: Settings;
|
||||
viewport: powerbi.IViewport;
|
||||
viewport: powerbiVisualsApi.IViewport;
|
||||
}
|
||||
|
||||
export class DataConverter implements IConverter<IDataConverterOptions, IDataRepresentation> {
|
||||
|
@ -99,7 +100,7 @@ export class DataConverter implements IConverter<IDataConverterOptions, IDataRep
|
|||
const dataRepresentation: IDataRepresentation = this.getDefaultData(settings.kpi.percentCalcDate);
|
||||
|
||||
if (this.isDataViewValid(dataView)) {
|
||||
const columns: Array<powerbi.DataViewValueColumn | powerbi.DataViewCategoryColumn> = [
|
||||
const columns: (powerbiVisualsApi.DataViewValueColumn | powerbiVisualsApi.DataViewCategoryColumn)[] = [
|
||||
...this.getColumns(dataView.categorical.categories),
|
||||
...this.getColumns(dataView.categorical.values),
|
||||
];
|
||||
|
@ -154,7 +155,7 @@ export class DataConverter implements IConverter<IDataConverterOptions, IDataRep
|
|||
return closestDataPoint || defaultDataPoint;
|
||||
}
|
||||
|
||||
public isDataViewValid(dataView: powerbi.DataView): boolean {
|
||||
public isDataViewValid(dataView: powerbiVisualsApi.DataView): boolean {
|
||||
return !!(dataView
|
||||
&& dataView.categorical
|
||||
&& dataView.categorical.categories
|
||||
|
@ -167,12 +168,12 @@ export class DataConverter implements IConverter<IDataConverterOptions, IDataRep
|
|||
}
|
||||
|
||||
protected getColumnGroupByRole(
|
||||
columns: Array<powerbi.DataViewValueColumn | powerbi.DataViewCategoryColumn>,
|
||||
columns: (powerbiVisualsApi.DataViewValueColumn | powerbiVisualsApi.DataViewCategoryColumn)[],
|
||||
index: number,
|
||||
): IColumnGroupByRole {
|
||||
const columnGroups: IColumnGroupByRole = {};
|
||||
|
||||
columns.forEach((column: powerbi.DataViewValueColumn | powerbi.DataViewCategoryColumn, valueIndex: number) => {
|
||||
columns.forEach((column: powerbiVisualsApi.DataViewValueColumn | powerbiVisualsApi.DataViewCategoryColumn, valueIndex: number) => {
|
||||
Object.keys(column.source.roles)
|
||||
.forEach((roleName: string) => {
|
||||
if (!columnGroups[roleName]) {
|
||||
|
@ -192,10 +193,10 @@ export class DataConverter implements IConverter<IDataConverterOptions, IDataRep
|
|||
}
|
||||
|
||||
private getColumns<Type>(columns: Type[]): Type[] {
|
||||
return columns.map((column) => column); // TODO: Why is it using map to create a copy of array?
|
||||
return columns.map((column) => column);
|
||||
}
|
||||
|
||||
private getViewportSize(viewport: powerbi.IViewport): ViewportSize {
|
||||
private getViewportSize(viewport: powerbiVisualsApi.IViewport): ViewportSize {
|
||||
if (viewport.height < 120 || viewport.width < 120) {
|
||||
return ViewportSize.tiny;
|
||||
} else if (viewport.height < 180 || viewport.width < 300) {
|
||||
|
@ -229,7 +230,7 @@ export class DataConverter implements IConverter<IDataConverterOptions, IDataRep
|
|||
dataRepresentation: IDataRepresentation,
|
||||
columnGroupByRole: IColumnGroupByRole,
|
||||
settings: Settings,
|
||||
valuesColumn: powerbi.DataViewValueColumns,
|
||||
valuesColumn: powerbiVisualsApi.DataViewValueColumns,
|
||||
): void {
|
||||
const dateColumnGroup: IColumnGroup = columnGroupByRole[dateColumn.name];
|
||||
const valueColumnGroup: IColumnGroup = columnGroupByRole[valueColumn.name];
|
||||
|
@ -252,159 +253,175 @@ export class DataConverter implements IConverter<IDataConverterOptions, IDataRep
|
|||
createSelectionIdBuilder,
|
||||
} = this.constructorOptions;
|
||||
|
||||
valueColumnGroup.columns.forEach((column: powerbi.DataViewValueColumn, columnIndex: number) => {
|
||||
const x: Date = dateColumnGroup.values[0] as Date;
|
||||
valueColumnGroup.columns.forEach((column: powerbiVisualsApi.DataViewValueColumn, columnIndex: number) => {
|
||||
const x: Date = <Date>(dateColumnGroup.values[0]);
|
||||
|
||||
if (x instanceof Date && x !== undefined && x !== null) {
|
||||
if (!dataRepresentation.series[columnIndex]) {
|
||||
const selectionId: powerbi.visuals.ISelectionId = createSelectionIdBuilder()
|
||||
const selectionId: powerbiVisualsApi.visuals.ISelectionId = createSelectionIdBuilder()
|
||||
.withSeries(valuesColumn, column)
|
||||
.withMeasure(column.source.queryName)
|
||||
.createSelectionId();
|
||||
|
||||
const seriesSettings: SeriesSettings = SeriesSettings.getDefault() as SeriesSettings;
|
||||
|
||||
for (const propertyName in seriesSettings) {
|
||||
const descriptor: BaseDescriptor = seriesSettings[propertyName];
|
||||
const defaultDescriptor: BaseDescriptor = settings[propertyName];
|
||||
|
||||
if (descriptor && descriptor.applyDefault && defaultDescriptor) {
|
||||
descriptor.applyDefault(defaultDescriptor);
|
||||
}
|
||||
}
|
||||
|
||||
seriesSettings.parseObjects(column.source.objects);
|
||||
|
||||
seriesSettings.values.setColumnFormat(column.source.format);
|
||||
seriesSettings.yAxis.setColumnFormat(column.source.format);
|
||||
seriesSettings.sparklineValue.setColumnFormat(column.source.format);
|
||||
seriesSettings.sparklineYAxis.setColumnFormat(column.source.format);
|
||||
|
||||
const series: IDataRepresentationSeries = {
|
||||
current: undefined,
|
||||
dateDifference: undefined,
|
||||
formattedDate: "",
|
||||
formattedTooltip: undefined,
|
||||
formattedVariance: "",
|
||||
index: dataRepresentation.series.length,
|
||||
name: column.source.displayName,
|
||||
points: [],
|
||||
const seriesSettings: SeriesSettings = this.prepareSeriesSettings(settings, column);
|
||||
const series = this.initDataRepresentationSeries(
|
||||
selectionId,
|
||||
settings: seriesSettings,
|
||||
smoothedPoints: [],
|
||||
x: {
|
||||
initialMax: undefined,
|
||||
initialMin: undefined,
|
||||
max: undefined,
|
||||
min: undefined,
|
||||
scale: DataRepresentationScale.create(),
|
||||
},
|
||||
y: {
|
||||
initialMax: undefined,
|
||||
initialMin: undefined,
|
||||
max: undefined,
|
||||
min: undefined,
|
||||
scale: DataRepresentationScale.create(),
|
||||
},
|
||||
ySparkline: {
|
||||
initialMax: undefined,
|
||||
initialMin: undefined,
|
||||
max: undefined,
|
||||
min: undefined,
|
||||
scale: DataRepresentationScale.create(),
|
||||
},
|
||||
|
||||
tooltip: undefined,
|
||||
variance: undefined,
|
||||
};
|
||||
dataRepresentation.series.length,
|
||||
column.source.displayName,
|
||||
seriesSettings,
|
||||
);
|
||||
|
||||
dataRepresentation.series.push(series);
|
||||
dataRepresentation.sortedSeries.push(series);
|
||||
}
|
||||
|
||||
const seriesItem: IDataRepresentationSeries = dataRepresentation.series[columnIndex];
|
||||
const y: number = this.parseValue(
|
||||
valueColumnGroup.values[columnIndex],
|
||||
dataRepresentation.series[columnIndex].settings.values.treatEmptyValuesAsZero,
|
||||
seriesItem.settings.values.treatEmptyValuesAsZero,
|
||||
);
|
||||
|
||||
const dataPoint: IDataRepresentationPoint = {
|
||||
index: dataRepresentation.series[columnIndex].points.length,
|
||||
index: seriesItem.points.length,
|
||||
x,
|
||||
y,
|
||||
};
|
||||
|
||||
dataRepresentation.latestDate = x;
|
||||
|
||||
dataRepresentation.series[columnIndex].points.push(dataPoint);
|
||||
if (seriesItem.points.length > 1 && (seriesItem.points[seriesItem.points.length - 1].y !== dataPoint.y)) {
|
||||
seriesItem.isLine = false;
|
||||
}
|
||||
|
||||
if (dataRepresentation.series[columnIndex].settings.values.showLatterAvailableValue) {
|
||||
seriesItem.points.push(dataPoint);
|
||||
|
||||
if (seriesItem.settings.values.showLatterAvailableValue) {
|
||||
if (!isNaN(dataPoint.y)) {
|
||||
dataRepresentation.series[columnIndex].current = dataPoint;
|
||||
seriesItem.current = dataPoint;
|
||||
}
|
||||
} else {
|
||||
dataRepresentation.series[columnIndex].current = dataPoint;
|
||||
seriesItem.current = dataPoint;
|
||||
}
|
||||
|
||||
dataRepresentation.series[columnIndex].x.min = this.getMin(
|
||||
dataRepresentation.series[columnIndex].x.min,
|
||||
x,
|
||||
);
|
||||
|
||||
dataRepresentation.series[columnIndex].x.max = this.getMax(
|
||||
dataRepresentation.series[columnIndex].x.max,
|
||||
x,
|
||||
);
|
||||
|
||||
if (!isNaN(y)) {
|
||||
dataRepresentation.series[columnIndex].y.min = this.getMin(
|
||||
dataRepresentation.series[columnIndex].y.min,
|
||||
y,
|
||||
);
|
||||
|
||||
dataRepresentation.series[columnIndex].y.max = this.getMax(
|
||||
dataRepresentation.series[columnIndex].y.max,
|
||||
y,
|
||||
);
|
||||
|
||||
dataRepresentation.series[columnIndex].ySparkline.min = this.getMin(
|
||||
dataRepresentation.series[columnIndex].ySparkline.min,
|
||||
y,
|
||||
);
|
||||
|
||||
dataRepresentation.series[columnIndex].ySparkline.max = this.getMax(
|
||||
dataRepresentation.series[columnIndex].ySparkline.max,
|
||||
y,
|
||||
);
|
||||
}
|
||||
|
||||
const tooltip: string = tooltipColumnGroup
|
||||
&& tooltipColumnGroup.values
|
||||
&& tooltipColumnGroup.values[columnIndex]
|
||||
|| undefined;
|
||||
|
||||
seriesItem.x.min = this.getMin(seriesItem.x.min, x);
|
||||
seriesItem.x.max = this.getMax(seriesItem.x.max, x);
|
||||
this.setupYMinMax(y, seriesItem);
|
||||
const tooltip: string = tooltipColumnGroup && tooltipColumnGroup.values && tooltipColumnGroup.values[columnIndex] || undefined;
|
||||
dataRepresentation.series[columnIndex].tooltip = tooltip;
|
||||
}
|
||||
});
|
||||
|
||||
const warningColumnGroup: IColumnGroup = columnGroupByRole[warningStateColumn.name];
|
||||
const subtitleColumnGroup: IColumnGroup = columnGroupByRole[subtitleColumn.name];
|
||||
if (subtitleColumnGroup) {
|
||||
dataRepresentation.subtitle = subtitleColumnGroup.values[0];
|
||||
}
|
||||
|
||||
const warningColumnGroup: IColumnGroup = columnGroupByRole[warningStateColumn.name];
|
||||
if (warningColumnGroup) {
|
||||
dataRepresentation.warningState = warningColumnGroup.values[0];
|
||||
}
|
||||
|
||||
const changeStartDateColumnGroup: IColumnGroup = columnGroupByRole[changeStartDateColumn.name];
|
||||
|
||||
if (changeStartDateColumnGroup) {
|
||||
const date: Date = changeStartDateColumnGroup
|
||||
&& changeStartDateColumnGroup.values
|
||||
&& changeStartDateColumnGroup.values[0];
|
||||
|
||||
dataRepresentation.percentCalcDate = date instanceof Date
|
||||
? date
|
||||
: dataRepresentation.percentCalcDate;
|
||||
dataRepresentation.percentCalcDate = date instanceof Date ? date : dataRepresentation.percentCalcDate;
|
||||
}
|
||||
}
|
||||
|
||||
private setupYMinMax(y: number, seriesItem: IDataRepresentationSeries): void {
|
||||
if (!isNaN(y)) {
|
||||
seriesItem.y.min = this.getMin(
|
||||
seriesItem.y.min,
|
||||
y,
|
||||
);
|
||||
|
||||
seriesItem.y.max = this.getMax(
|
||||
seriesItem.y.max,
|
||||
y,
|
||||
);
|
||||
|
||||
seriesItem.ySparkline.min = this.getMin(
|
||||
seriesItem.ySparkline.min,
|
||||
y,
|
||||
);
|
||||
|
||||
seriesItem.ySparkline.max = this.getMax(
|
||||
seriesItem.ySparkline.max,
|
||||
y,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
private prepareSeriesSettings(settings: Settings, column: powerbiVisualsApi.DataViewValueColumn): SeriesSettings {
|
||||
const seriesSettings: SeriesSettings = <SeriesSettings>(SeriesSettings.getDefault());
|
||||
|
||||
for (const propertyName of Object.keys(seriesSettings)) {
|
||||
const descriptor: BaseDescriptor = seriesSettings[propertyName];
|
||||
const defaultDescriptor: BaseDescriptor = settings[propertyName];
|
||||
|
||||
if (descriptor && descriptor.applyDefault && defaultDescriptor) {
|
||||
descriptor.applyDefault(defaultDescriptor);
|
||||
}
|
||||
}
|
||||
|
||||
seriesSettings.parseObjects(column.source.objects);
|
||||
seriesSettings.values.setColumnFormat(column.source.format);
|
||||
seriesSettings.yAxis.setColumnFormat(column.source.format);
|
||||
seriesSettings.sparklineValue.setColumnFormat(column.source.format);
|
||||
seriesSettings.sparklineYAxis.setColumnFormat(column.source.format);
|
||||
|
||||
return seriesSettings;
|
||||
}
|
||||
|
||||
private initDataRepresentationSeries(
|
||||
selectionId: powerbiVisualsApi.visuals.ISelectionId,
|
||||
seriesLength: number,
|
||||
sourceDispalyName: string,
|
||||
seriesSettings: SeriesSettings
|
||||
): IDataRepresentationSeries {
|
||||
return {
|
||||
current: undefined,
|
||||
dateDifference: undefined,
|
||||
formattedDate: "",
|
||||
formattedTooltip: undefined,
|
||||
formattedVariance: "",
|
||||
index: seriesLength,
|
||||
isLine: true,
|
||||
name: sourceDispalyName,
|
||||
points: [],
|
||||
selectionId,
|
||||
settings: seriesSettings,
|
||||
smoothedPoints: [],
|
||||
x: {
|
||||
initialMax: undefined,
|
||||
initialMin: undefined,
|
||||
max: undefined,
|
||||
min: undefined,
|
||||
scale: DataRepresentationScale.CREATE(),
|
||||
},
|
||||
y: {
|
||||
initialMax: undefined,
|
||||
initialMin: undefined,
|
||||
max: undefined,
|
||||
min: undefined,
|
||||
scale: DataRepresentationScale.CREATE(),
|
||||
},
|
||||
ySparkline: {
|
||||
initialMax: undefined,
|
||||
initialMin: undefined,
|
||||
max: undefined,
|
||||
min: undefined,
|
||||
scale: DataRepresentationScale.CREATE(),
|
||||
},
|
||||
|
||||
tooltip: undefined,
|
||||
variance: undefined,
|
||||
};
|
||||
}
|
||||
|
||||
private postProcessData(dataRepresentation: IDataRepresentation, settings: Settings): void {
|
||||
dataRepresentation.staleDateDifference = 0;
|
||||
|
||||
|
@ -494,19 +511,19 @@ export class DataConverter implements IConverter<IDataConverterOptions, IDataRep
|
|||
axis: IDataRepresentationAxis,
|
||||
axisDescriptor: AxisDescriptor,
|
||||
) {
|
||||
if ((!isNaN(axisDescriptor.min as number) && axisDescriptor.min !== null)
|
||||
|| (!isNaN(axisDescriptor.defaultMin as number) && axisDescriptor.defaultMin !== null)) {
|
||||
if ((!isNaN(<number>(axisDescriptor.min)) && axisDescriptor.min !== null)
|
||||
|| (!isNaN(<number>(axisDescriptor.defaultMin)) && axisDescriptor.defaultMin !== null)) {
|
||||
axis.min = axisDescriptor.getMin();
|
||||
}
|
||||
else if (!isNaN(axis.min as number) && axis.min !== null) {
|
||||
else if (!isNaN(<number>(axis.min)) && axis.min !== null) {
|
||||
axisDescriptor.defaultMin = axis.min;
|
||||
}
|
||||
|
||||
if ((!isNaN(axisDescriptor.max as number) && axisDescriptor.max !== null)
|
||||
|| (!isNaN(axisDescriptor.defaultMax as number) && axisDescriptor.defaultMax !== null)) {
|
||||
if ((!isNaN(<number>(axisDescriptor.max)) && axisDescriptor.max !== null)
|
||||
|| (!isNaN(<number>(axisDescriptor.defaultMax)) && axisDescriptor.defaultMax !== null)) {
|
||||
axis.max = axisDescriptor.getMax();
|
||||
} else if (!isNaN(axis.max as number) && axis.max !== null) {
|
||||
axisDescriptor.defaultMax = (axis.max as number) + (axis.max as number) * this.increasedDomainValueInPercentage;
|
||||
} else if (!isNaN(<number>(axis.max)) && axis.max !== null) {
|
||||
axisDescriptor.defaultMax = (<number>(axis.max)) + (<number>(axis.max)) * this.increasedDomainValueInPercentage;
|
||||
axis.max = axisDescriptor.defaultMax;
|
||||
}
|
||||
|
||||
|
|
|
@ -26,7 +26,7 @@
|
|||
|
||||
import { displayUnitSystemType, valueFormatter } from "powerbi-visuals-utils-formattingutils";
|
||||
import { NumericDescriptor } from "../../settings/descriptors/numericDescriptor";
|
||||
import { isValueValid } from "../../utils/valueUtils";
|
||||
import { isValueValid } from "../../utils/isValueValid";
|
||||
|
||||
const wholeUnits: displayUnitSystemType.DisplayUnitSystemType = displayUnitSystemType.DisplayUnitSystemType.WholeUnits;
|
||||
|
||||
|
@ -55,7 +55,16 @@ export function getValueFormatter(value: number, settings: NumericDescriptor): v
|
|||
return valueFormatter.create({
|
||||
displayUnitSystemType: wholeUnits,
|
||||
format: settings.getFormat(),
|
||||
precision: settings.precision,
|
||||
precision: detectPrecision(value, settings),
|
||||
value: settings.displayUnits || value,
|
||||
});
|
||||
}
|
||||
|
||||
export function detectPrecision(inputValue: number, settings: NumericDescriptor): number {
|
||||
if (settings.autoPrecision) {
|
||||
const format = settings.format || settings.defaultFormat || settings.columnFormat;
|
||||
return valueFormatter.calculateExactDigitsPrecision(inputValue, format, settings.displayUnits, 3);
|
||||
}
|
||||
|
||||
return settings.precision;
|
||||
}
|
||||
|
|
|
@ -24,7 +24,7 @@
|
|||
* THE SOFTWARE.
|
||||
*/
|
||||
|
||||
import powerbi from "powerbi-visuals-api";
|
||||
import powerbiVisualsApi from "powerbi-visuals-api";
|
||||
|
||||
import { SeriesSettings } from "../../settings/seriesSettings";
|
||||
import { DataRepresentationScale } from "./dataRepresentationScale";
|
||||
|
@ -66,8 +66,9 @@ export interface IDataRepresentationSeries {
|
|||
staleDateDifference?: number;
|
||||
tooltip: string;
|
||||
formattedTooltip: string;
|
||||
selectionId: powerbi.visuals.ISelectionId;
|
||||
selectionId: powerbiVisualsApi.visuals.ISelectionId;
|
||||
settings: SeriesSettings;
|
||||
isLine: boolean; // There is a bag in SVG that hide line that has gradient attachment. The mark indicate to apply workaround
|
||||
}
|
||||
|
||||
export enum ViewportSize {
|
||||
|
@ -87,7 +88,7 @@ export interface IDataRepresentation {
|
|||
latestDate: Date;
|
||||
staleDateDifference?: number;
|
||||
percentCalcDate: Date;
|
||||
|
||||
viewport: powerbi.IViewport;
|
||||
subtitle?: string;
|
||||
viewport: powerbiVisualsApi.IViewport;
|
||||
viewportSize: ViewportSize;
|
||||
}
|
||||
|
|
|
@ -44,7 +44,7 @@ export enum DataRepresentationTypeEnum {
|
|||
}
|
||||
|
||||
export class DataRepresentationScale {
|
||||
public static create(): DataRepresentationScale {
|
||||
public static CREATE(): DataRepresentationScale {
|
||||
return new DataRepresentationScale();
|
||||
}
|
||||
|
||||
|
@ -75,7 +75,7 @@ export class DataRepresentationScale {
|
|||
}
|
||||
|
||||
if (scale) {
|
||||
(scale as any).domain(values); // "as any" is some kind of hack for TS to build it well
|
||||
(<any>scale).domain(values); // "as any" is some kind of hack for TS to build it well
|
||||
}
|
||||
|
||||
this.baseScale = scale;
|
||||
|
@ -97,7 +97,7 @@ export class DataRepresentationScale {
|
|||
return 0;
|
||||
}
|
||||
|
||||
return (this.baseScale as any)(value); // "as any" is some kind of hack for TS to build it well
|
||||
return (<any>(this.baseScale))(value); // "as any" is some kind of hack for TS to build it well
|
||||
}
|
||||
|
||||
public copy(): DataRepresentationScale {
|
||||
|
|
|
@ -33,6 +33,12 @@ export interface ISmoothConstructorOptions {
|
|||
accuracy?: number;
|
||||
}
|
||||
|
||||
interface IAlphaBeta {
|
||||
alpha: number;
|
||||
beta: number;
|
||||
w: number;
|
||||
}
|
||||
|
||||
export class SmoothDataConverter implements IConverter<IDataRepresentationPoint[], IDataRepresentationPoint[]> {
|
||||
private bandwidth: number = 0.1;
|
||||
private robustnessIters: number = 2;
|
||||
|
@ -48,9 +54,7 @@ export class SmoothDataConverter implements IConverter<IDataRepresentationPoint[
|
|||
|
||||
public convert(points: IDataRepresentationPoint[]): IDataRepresentationPoint[] {
|
||||
const length = points.length;
|
||||
|
||||
const bandwidthInPoints = Math.floor(this.bandwidth * length);
|
||||
|
||||
const resultPoints: IDataRepresentationPoint[] = [];
|
||||
const residuals: number[] = [];
|
||||
const robustnessWeights: number[] = [];
|
||||
|
@ -60,7 +64,7 @@ export class SmoothDataConverter implements IConverter<IDataRepresentationPoint[
|
|||
robustnessWeights[i] = 1;
|
||||
}
|
||||
|
||||
let w: number;
|
||||
let alphaBeta: IAlphaBeta;
|
||||
|
||||
for (let iter: number = 0; iter < this.robustnessIters; iter++) {
|
||||
const bandwidthInterval: number[] = [0, bandwidthInPoints - 1];
|
||||
|
@ -81,49 +85,12 @@ export class SmoothDataConverter implements IConverter<IDataRepresentationPoint[
|
|||
&& ileft >= 0
|
||||
&& iright >= 0
|
||||
) {
|
||||
const edge: number = (points[i].x.getTime() - points[ileft].x.getTime())
|
||||
> (points[iright].x.getTime() - points[i].x.getTime())
|
||||
? ileft
|
||||
: iright;
|
||||
alphaBeta = this.calcAlphaBeta(robustnessWeights, i, x, ileft, iright, points);
|
||||
|
||||
let sumWeights: number = 0;
|
||||
let sumX: number = 0;
|
||||
let sumXSquared: number = 0;
|
||||
let sumY: number = 0;
|
||||
let sumXY: number = 0;
|
||||
|
||||
const denom: number = Math.abs(1 / (points[edge].x.getTime() - x));
|
||||
|
||||
for (let k: number = ileft; k <= iright; ++k) {
|
||||
const xk: number = points[k].x.getTime();
|
||||
const yk: number = points[k].y;
|
||||
const dist: number = k < i ? x - xk : xk - x;
|
||||
|
||||
w = this.science_stats_loessTricube(dist * denom) * robustnessWeights[k];
|
||||
|
||||
const xkw: number = xk * w;
|
||||
|
||||
sumWeights += w;
|
||||
sumX += xkw;
|
||||
sumXSquared += xk * xkw;
|
||||
sumY += yk * w;
|
||||
sumXY += yk * xkw;
|
||||
}
|
||||
|
||||
const meanX: number = sumX / sumWeights;
|
||||
const meanY: number = sumY / sumWeights;
|
||||
const meanXY: number = sumXY / sumWeights;
|
||||
const meanXSquared: number = sumXSquared / sumWeights;
|
||||
|
||||
const beta: number = (Math.sqrt(Math.abs(meanXSquared - meanX * meanX)) < this.accuracy)
|
||||
? 0 : ((meanXY - meanX * meanY) / (meanXSquared - meanX * meanX));
|
||||
|
||||
const alpha: number = meanY - beta * meanX;
|
||||
|
||||
if (!isNaN(beta) && !isNaN(alpha)) {
|
||||
if (!isNaN(alphaBeta.beta) && !isNaN(alphaBeta.alpha)) {
|
||||
resultPoints[i] = {
|
||||
...points[i],
|
||||
y: beta * x + alpha,
|
||||
y: alphaBeta.beta * x + alphaBeta.alpha,
|
||||
};
|
||||
} else {
|
||||
resultPoints[i] = { ...points[i] };
|
||||
|
@ -151,13 +118,67 @@ export class SmoothDataConverter implements IConverter<IDataRepresentationPoint[
|
|||
|
||||
for (let i: number = 0; i < length; i++) {
|
||||
const arg: number = residuals[i] / (6 * medianResidual);
|
||||
robustnessWeights[i] = (arg >= 1) ? 0 : ((w = 1 - arg * arg) * w);
|
||||
robustnessWeights[i] = (arg >= 1) ? 0 : ((alphaBeta.w = 1 - arg * arg) * alphaBeta.w);
|
||||
}
|
||||
}
|
||||
|
||||
return resultPoints;
|
||||
}
|
||||
|
||||
private calcAlphaBeta(
|
||||
robustnessWeights: number[],
|
||||
itrn: number,
|
||||
timePoint: number,
|
||||
ileft: number,
|
||||
iright: number,
|
||||
points: IDataRepresentationPoint[],
|
||||
): IAlphaBeta {
|
||||
const edge: number = (points[itrn].x.getTime() - points[ileft].x.getTime())
|
||||
> (points[iright].x.getTime() - points[itrn].x.getTime())
|
||||
? ileft
|
||||
: iright;
|
||||
let w: number;
|
||||
let sumWeights: number = 0;
|
||||
let sumX: number = 0;
|
||||
let sumXSquared: number = 0;
|
||||
let sumY: number = 0;
|
||||
let sumXY: number = 0;
|
||||
|
||||
const denom: number = Math.abs(1 / (points[edge].x.getTime() - timePoint));
|
||||
|
||||
for (let k: number = ileft; k <= iright; ++k) {
|
||||
const xk: number = points[k].x.getTime();
|
||||
const yk: number = points[k].y;
|
||||
const dist: number = k < itrn ? timePoint - xk : xk - timePoint;
|
||||
|
||||
w = this.science_stats_loessTricube(dist * denom) * robustnessWeights[k];
|
||||
|
||||
const xkw: number = xk * w;
|
||||
|
||||
sumWeights += w;
|
||||
sumX += xkw;
|
||||
sumXSquared += xk * xkw;
|
||||
sumY += yk * w;
|
||||
sumXY += yk * xkw;
|
||||
}
|
||||
|
||||
const meanX: number = sumX / sumWeights;
|
||||
const meanY: number = sumY / sumWeights;
|
||||
const meanXY: number = sumXY / sumWeights;
|
||||
const meanXSquared: number = sumXSquared / sumWeights;
|
||||
|
||||
const beta: number = (Math.sqrt(Math.abs(meanXSquared - meanX * meanX)) < this.accuracy)
|
||||
? 0 : ((meanXY - meanX * meanY) / (meanXSquared - meanX * meanX));
|
||||
|
||||
const alpha: number = meanY - beta * meanX;
|
||||
|
||||
return {
|
||||
alpha,
|
||||
beta,
|
||||
w
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Computes the tricube weight function.
|
||||
* https://en.wikipedia.org/wiki/Local_regression#Weight_function
|
||||
|
|
|
@ -26,7 +26,10 @@
|
|||
|
||||
import "../styles/styles.less";
|
||||
|
||||
import powerbi from "powerbi-visuals-api";
|
||||
import "regenerator-runtime/runtime.js";
|
||||
import powerbiVisualsApi from "powerbi-visuals-api";
|
||||
|
||||
import ISelectionManager = powerbiVisualsApi.extensibility.ISelectionManager;
|
||||
|
||||
import { dispatch, Dispatch } from "d3-dispatch";
|
||||
import { select as d3Select } from "d3-selection";
|
||||
|
@ -52,25 +55,25 @@ import { ScaleService } from "./services/scaleService";
|
|||
// powerbi.extensibility.utils.tooltip
|
||||
import { ITooltipServiceWrapper, TooltipServiceWrapper } from "powerbi-visuals-utils-tooltiputils";
|
||||
|
||||
export class MultiKpi implements powerbi.extensibility.visual.IVisual {
|
||||
export class MultiKpi implements powerbiVisualsApi.extensibility.visual.IVisual {
|
||||
private dataConverter: DataConverter;
|
||||
|
||||
private minViewport: powerbi.IViewport = {
|
||||
private minViewport: powerbiVisualsApi.IViewport = {
|
||||
height: 95,
|
||||
width: 200,
|
||||
};
|
||||
|
||||
private dataRepresentation: IDataRepresentation;
|
||||
private settings: Settings;
|
||||
private viewport: powerbi.IViewport;
|
||||
|
||||
private rootComponent: IVisualComponent<IVisualComponentRenderOptions>;
|
||||
|
||||
private viewport: powerbiVisualsApi.IViewport;
|
||||
private eventDispatcher: Dispatch<any> = dispatch(...Object.keys(EventName));
|
||||
|
||||
private tooltipServiceWrapper: ITooltipServiceWrapper;
|
||||
private host: powerbiVisualsApi.extensibility.visual.IVisualHost;
|
||||
private selectionManager: ISelectionManager;
|
||||
|
||||
constructor(options: powerbi.extensibility.visual.VisualConstructorOptions) {
|
||||
public rootComponent: IVisualComponent<IVisualComponentRenderOptions>;
|
||||
|
||||
constructor(options: powerbiVisualsApi.extensibility.visual.VisualConstructorOptions) {
|
||||
if (window.location !== window.parent.location) {
|
||||
require("core-js/stable");
|
||||
}
|
||||
|
@ -80,6 +83,8 @@ export class MultiKpi implements powerbi.extensibility.visual.IVisual {
|
|||
host,
|
||||
} = options;
|
||||
|
||||
this.host = host;
|
||||
|
||||
this.tooltipServiceWrapper = new TooltipServiceWrapper(
|
||||
{
|
||||
handleTouchDelay: 0,
|
||||
|
@ -113,35 +118,54 @@ export class MultiKpi implements powerbi.extensibility.visual.IVisual {
|
|||
style: host.colorPalette,
|
||||
tooltipServiceWrapper: this.tooltipServiceWrapper,
|
||||
});
|
||||
|
||||
this.selectionManager = this.host.createSelectionManager();
|
||||
|
||||
const visualSelection = d3Select(element);
|
||||
visualSelection.on("contextmenu", (event) => {
|
||||
let dataPoint: any = d3Select(event.target).datum();
|
||||
this.selectionManager.showContextMenu(dataPoint ? dataPoint.selectionId : {}, {
|
||||
x: event.clientX,
|
||||
y: event.clientY
|
||||
});
|
||||
event.preventDefault();
|
||||
});
|
||||
}
|
||||
|
||||
public update(options: powerbi.extensibility.visual.VisualUpdateOptions) {
|
||||
public update(options: powerbiVisualsApi.extensibility.visual.VisualUpdateOptions) {
|
||||
if (!this.dataConverter || !this.rootComponent) {
|
||||
return;
|
||||
}
|
||||
|
||||
const dataView: powerbi.DataView = options
|
||||
&& options.dataViews
|
||||
&& options.dataViews[0];
|
||||
try {
|
||||
this.host.eventService.renderingStarted(options);
|
||||
|
||||
this.viewport = this.getViewport(options && options.viewport);
|
||||
const dataView: powerbiVisualsApi.DataView = options
|
||||
&& options.dataViews
|
||||
&& options.dataViews[0];
|
||||
|
||||
this.settings = Settings.parseSettings(dataView) as Settings;
|
||||
this.viewport = this.getViewport(options && options.viewport);
|
||||
|
||||
this.dataRepresentation = this.dataConverter.convert({
|
||||
dataView,
|
||||
settings: this.settings,
|
||||
viewport: this.viewport,
|
||||
});
|
||||
this.settings = <Settings>(Settings.PARSE_SETTINGS(dataView));
|
||||
|
||||
this.render(
|
||||
this.dataRepresentation,
|
||||
this.settings,
|
||||
this.viewport,
|
||||
);
|
||||
this.dataRepresentation = this.dataConverter.convert({
|
||||
dataView,
|
||||
settings: this.settings,
|
||||
viewport: this.viewport,
|
||||
});
|
||||
|
||||
this.render(
|
||||
this.dataRepresentation,
|
||||
this.settings,
|
||||
this.viewport,
|
||||
);
|
||||
} catch (ex) {
|
||||
this.host.eventService.renderingFailed(options, JSON.stringify(ex));
|
||||
}
|
||||
this.host.eventService.renderingFinished(options);
|
||||
}
|
||||
|
||||
public enumerateObjectInstances(options: powerbi.EnumerateVisualObjectInstancesOptions): powerbi.VisualObjectInstanceEnumeration {
|
||||
public enumerateObjectInstances(options: powerbiVisualsApi.EnumerateVisualObjectInstancesOptions): powerbiVisualsApi.VisualObjectInstanceEnumeration {
|
||||
if (!this.settings) {
|
||||
return [];
|
||||
}
|
||||
|
@ -154,7 +178,7 @@ export class MultiKpi implements powerbi.extensibility.visual.IVisual {
|
|||
return this.settings.enumerateObjectInstances(options);
|
||||
}
|
||||
|
||||
const enumerationObject: powerbi.VisualObjectInstanceEnumerationObject
|
||||
const enumerationObject: powerbiVisualsApi.VisualObjectInstanceEnumerationObject
|
||||
= this.settings.enumerateObjectInstancesWithSelectionId(
|
||||
options,
|
||||
"[All]",
|
||||
|
@ -178,7 +202,7 @@ export class MultiKpi implements powerbi.extensibility.visual.IVisual {
|
|||
private render(
|
||||
data: IDataRepresentation,
|
||||
settings: Settings,
|
||||
viewport: powerbi.IViewport,
|
||||
viewport: powerbiVisualsApi.IViewport,
|
||||
): void {
|
||||
this.rootComponent.render({
|
||||
data,
|
||||
|
@ -187,7 +211,7 @@ export class MultiKpi implements powerbi.extensibility.visual.IVisual {
|
|||
});
|
||||
}
|
||||
|
||||
private getViewport(currentViewport: powerbi.IViewport): powerbi.IViewport {
|
||||
private getViewport(currentViewport: powerbiVisualsApi.IViewport): powerbiVisualsApi.IViewport {
|
||||
if (!currentViewport) {
|
||||
return { ...this.minViewport };
|
||||
}
|
|
@ -24,12 +24,12 @@
|
|||
* THE SOFTWARE.
|
||||
*/
|
||||
|
||||
import powerbi from "powerbi-visuals-api";
|
||||
import powerbiVisualsApi from "powerbi-visuals-api";
|
||||
|
||||
export class ScaleService {
|
||||
constructor(private rootElement: HTMLElement) { }
|
||||
|
||||
public getScale(): powerbi.IViewport {
|
||||
public getScale(): powerbiVisualsApi.IViewport {
|
||||
if (!this.rootElement) {
|
||||
return {
|
||||
height: 1,
|
||||
|
|
|
@ -34,6 +34,7 @@ export class NumericDescriptor
|
|||
public noValueLabel: string = "N/A";
|
||||
public displayUnits: number = 0;
|
||||
public precision: number = 0;
|
||||
public autoPrecision: boolean = false;
|
||||
|
||||
protected minPrecision: number = 0;
|
||||
protected maxPrecision: number = 17;
|
||||
|
|
|
@ -27,7 +27,7 @@
|
|||
import { BaseDescriptor } from "./baseDescriptor";
|
||||
|
||||
export class StaleDataDescriptor extends BaseDescriptor {
|
||||
public staleDataText: string = undefined;
|
||||
public staleDataText: string = "";
|
||||
public staleDataThreshold: number = 0;
|
||||
public deductThresholdDays: boolean = false;
|
||||
public color: string = "#3599b8";
|
||||
|
|
|
@ -34,6 +34,7 @@ import { SubtitleDescriptor } from "./descriptors/subtitleDescriptor";
|
|||
import { TooltipDescriptor } from "./descriptors/tooltipDescriptor";
|
||||
import { ValuesDescriptor } from "./descriptors/valuesDescriptor";
|
||||
import { VarianceDescriptor } from "./descriptors/varianceDescriptor";
|
||||
import { StaleDataDescriptor } from "./descriptors/staleDataDescriptor";
|
||||
|
||||
export class SeriesSettings extends SettingsBase {
|
||||
public values: ValuesDescriptor = new ValuesDescriptor();
|
||||
|
@ -45,4 +46,5 @@ export class SeriesSettings extends SettingsBase {
|
|||
public sparklineChart: SparklineChartDescriptor = new SparklineChartDescriptor();
|
||||
public sparklineYAxis: SparklineAxisDescriptor = new SparklineAxisDescriptor();
|
||||
public sparklineValue: SubtitleDescriptor = new SubtitleDescriptor();
|
||||
public staleData: StaleDataDescriptor = new StaleDataDescriptor();
|
||||
}
|
||||
|
|
|
@ -97,7 +97,7 @@ export class Settings extends SettingsBase {
|
|||
}
|
||||
|
||||
public parse(): void {
|
||||
if (this.staleData.staleDataText === undefined) {
|
||||
if (this.staleData.staleDataText === "") {
|
||||
this.staleData.staleDataText = "Data is ${1} days late." + (this.subtitle.staleDataText || "");
|
||||
}
|
||||
|
||||
|
|
|
@ -24,7 +24,7 @@
|
|||
* THE SOFTWARE.
|
||||
*/
|
||||
|
||||
import powerbi from "powerbi-visuals-api";
|
||||
import powerbiVisualsApi from "powerbi-visuals-api";
|
||||
|
||||
import {
|
||||
dataViewObjects,
|
||||
|
@ -38,12 +38,12 @@ import { IDescriptor } from "./descriptors/descriptor";
|
|||
import { BaseDescriptor } from "./descriptors/baseDescriptor";
|
||||
|
||||
export class SettingsBase extends dataViewObjectsParser.DataViewObjectsParser {
|
||||
public static parseSettings(options: powerbi.DataView): SettingsBase {
|
||||
public static PARSE_SETTINGS(options: powerbiVisualsApi.DataView): SettingsBase {
|
||||
const settings: SettingsBase = this.parse<SettingsBase>(options);
|
||||
|
||||
Object.keys(settings).forEach((descriptorName: string) => {
|
||||
if ((settings[descriptorName] as IDescriptor).parse) {
|
||||
(settings[descriptorName] as IDescriptor).parse();
|
||||
if ((<IDescriptor>settings[descriptorName]).parse) {
|
||||
(<IDescriptor>settings[descriptorName]).parse();
|
||||
}
|
||||
});
|
||||
|
||||
|
@ -56,12 +56,12 @@ export class SettingsBase extends dataViewObjectsParser.DataViewObjectsParser {
|
|||
// This function is required to update settings once they are parsed from a data view object
|
||||
}
|
||||
|
||||
public parseObjects(objects: powerbi.DataViewObjects): SettingsBase {
|
||||
public parseObjects(objects: powerbiVisualsApi.DataViewObjects): SettingsBase {
|
||||
if (objects) {
|
||||
const properties: dataViewObjectsParser.DataViewProperties = this.getProperties();
|
||||
|
||||
for (const objectName in properties) {
|
||||
for (const propertyName in properties[objectName]) {
|
||||
for (const objectName of Object.keys(properties)) {
|
||||
for (const propertyName of Object.keys(properties[objectName])) {
|
||||
const defaultValue: any = this[objectName][propertyName];
|
||||
|
||||
this[objectName][propertyName] = dataViewObjects.getCommonValue(
|
||||
|
@ -70,8 +70,8 @@ export class SettingsBase extends dataViewObjectsParser.DataViewObjectsParser {
|
|||
defaultValue);
|
||||
}
|
||||
|
||||
if ((this[objectName] as IDescriptor).parse) {
|
||||
(this[objectName] as IDescriptor).parse();
|
||||
if ((<IDescriptor>this[objectName]).parse) {
|
||||
(<IDescriptor>this[objectName]).parse();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -81,8 +81,8 @@ export class SettingsBase extends dataViewObjectsParser.DataViewObjectsParser {
|
|||
return this;
|
||||
}
|
||||
|
||||
public enumerateObjectInstances(options: powerbi.EnumerateVisualObjectInstancesOptions): powerbi.VisualObjectInstanceEnumerationObject {
|
||||
const enumerationObject: powerbi.VisualObjectInstanceEnumerationObject = {
|
||||
public enumerateObjectInstances(options: powerbiVisualsApi.EnumerateVisualObjectInstancesOptions): powerbiVisualsApi.VisualObjectInstanceEnumerationObject {
|
||||
const enumerationObject: powerbiVisualsApi.VisualObjectInstanceEnumerationObject = {
|
||||
containers: [],
|
||||
instances: [],
|
||||
};
|
||||
|
@ -101,18 +101,18 @@ export class SettingsBase extends dataViewObjectsParser.DataViewObjectsParser {
|
|||
}
|
||||
|
||||
public enumerateObjectInstancesWithSelectionId(
|
||||
options: powerbi.EnumerateVisualObjectInstancesOptions,
|
||||
options: powerbiVisualsApi.EnumerateVisualObjectInstancesOptions,
|
||||
displayName: string,
|
||||
selectionId: powerbi.visuals.ISelectionId,
|
||||
enumerationObject: powerbi.VisualObjectInstanceEnumerationObject = {
|
||||
selectionId: powerbiVisualsApi.visuals.ISelectionId,
|
||||
enumerationObject: powerbiVisualsApi.VisualObjectInstanceEnumerationObject = {
|
||||
containers: [],
|
||||
instances: [],
|
||||
},
|
||||
): powerbi.VisualObjectInstanceEnumerationObject {
|
||||
): powerbiVisualsApi.VisualObjectInstanceEnumerationObject {
|
||||
if (this.areOptionsValid(options)) {
|
||||
const { objectName } = options;
|
||||
|
||||
const selector: powerbi.data.Selector = selectionId && selectionId.getSelector();
|
||||
const selector: powerbiVisualsApi.data.Selector = selectionId && selectionId.getSelector();
|
||||
|
||||
this.addSettingsToContainer(
|
||||
objectName,
|
||||
|
@ -126,17 +126,17 @@ export class SettingsBase extends dataViewObjectsParser.DataViewObjectsParser {
|
|||
return enumerationObject;
|
||||
}
|
||||
|
||||
private areOptionsValid(options: powerbi.EnumerateVisualObjectInstancesOptions): boolean {
|
||||
private areOptionsValid(options: powerbiVisualsApi.EnumerateVisualObjectInstancesOptions): boolean {
|
||||
return !!(options
|
||||
&& options.objectName
|
||||
&& this[options.objectName]
|
||||
);
|
||||
}
|
||||
|
||||
private getSettings(settings: BaseDescriptor): { [propertyName: string]: powerbi.DataViewPropertyValue } {
|
||||
const properties: { [propertyName: string]: powerbi.DataViewPropertyValue; } = {};
|
||||
private getSettings(settings: BaseDescriptor): { [propertyName: string]: powerbiVisualsApi.DataViewPropertyValue } {
|
||||
const properties: { [propertyName: string]: powerbiVisualsApi.DataViewPropertyValue; } = {};
|
||||
|
||||
for (const descriptor in settings) {
|
||||
for (const descriptor of Object.keys(settings)) {
|
||||
const value: any = settings.getValueByPropertyName
|
||||
? settings.getValueByPropertyName(descriptor)
|
||||
: settings[descriptor];
|
||||
|
@ -159,9 +159,9 @@ export class SettingsBase extends dataViewObjectsParser.DataViewObjectsParser {
|
|||
private addSettingsToContainer(
|
||||
objectName: string,
|
||||
displayName: string,
|
||||
selector: powerbi.data.Selector,
|
||||
enumerationObject: powerbi.VisualObjectInstanceEnumerationObject,
|
||||
properties: { [propertyName: string]: powerbi.DataViewPropertyValue },
|
||||
selector: powerbiVisualsApi.data.Selector,
|
||||
enumerationObject: powerbiVisualsApi.VisualObjectInstanceEnumerationObject,
|
||||
properties: { [propertyName: string]: powerbiVisualsApi.DataViewPropertyValue },
|
||||
): void {
|
||||
const containerIdx: number = enumerationObject.containers.push({ displayName }) - 1;
|
||||
|
||||
|
@ -176,12 +176,12 @@ export class SettingsBase extends dataViewObjectsParser.DataViewObjectsParser {
|
|||
|
||||
private addSettingsToInstances(
|
||||
objectName: string,
|
||||
enumerationObject: powerbi.VisualObjectInstanceEnumerationObject,
|
||||
properties: { [propertyName: string]: powerbi.DataViewPropertyValue },
|
||||
selector: powerbi.data.Selector = null,
|
||||
enumerationObject: powerbiVisualsApi.VisualObjectInstanceEnumerationObject,
|
||||
properties: { [propertyName: string]: powerbiVisualsApi.DataViewPropertyValue },
|
||||
selector: powerbiVisualsApi.data.Selector = null,
|
||||
containerIdx?: number,
|
||||
): void {
|
||||
const instance: powerbi.VisualObjectInstance = {
|
||||
const instance: powerbiVisualsApi.VisualObjectInstance = {
|
||||
objectName,
|
||||
properties,
|
||||
selector,
|
||||
|
|
|
@ -24,7 +24,7 @@
|
|||
* THE SOFTWARE.
|
||||
*/
|
||||
|
||||
import powerbi from "powerbi-visuals-api";
|
||||
import powerbiVisualsApi from "powerbi-visuals-api";
|
||||
|
||||
import {
|
||||
CssConstants,
|
||||
|
@ -52,10 +52,10 @@ export abstract class BaseComponent<ConstructorOptionsType, RenderOptionsType> i
|
|||
protected renderOptions: RenderOptionsType;
|
||||
|
||||
protected minWidth: number = 20;
|
||||
protected width: number = undefined;
|
||||
protected width: number;
|
||||
|
||||
protected minHeight: number = 20;
|
||||
protected height: number = undefined;
|
||||
protected height: number;
|
||||
|
||||
private isComponentShown: boolean = true;
|
||||
private classNamePrefix: string = "multiKpi_";
|
||||
|
@ -136,7 +136,7 @@ export abstract class BaseComponent<ConstructorOptionsType, RenderOptionsType> i
|
|||
this.updateSizeOfElement(this.width, this.height);
|
||||
}
|
||||
|
||||
public getViewport(): powerbi.IViewport {
|
||||
public getViewport(): powerbiVisualsApi.IViewport {
|
||||
return {
|
||||
height: this.height,
|
||||
width: this.width,
|
||||
|
@ -186,7 +186,7 @@ export abstract class BaseComponent<ConstructorOptionsType, RenderOptionsType> i
|
|||
.remove();
|
||||
}
|
||||
|
||||
protected updateViewport(viewport: powerbi.IViewport): void {
|
||||
protected updateViewport(viewport: powerbiVisualsApi.IViewport): void {
|
||||
this.element
|
||||
.style("width", pixelConverter.toString(viewport.width))
|
||||
.style("height", pixelConverter.toString(viewport.height));
|
||||
|
|
|
@ -30,9 +30,13 @@ import { IVisualComponent } from "./visualComponent";
|
|||
export abstract class BaseContainerComponent<ConstructorOptionsType, RenderOptionsType, ComponentsRenderOptions>
|
||||
extends BaseComponent<ConstructorOptionsType, RenderOptionsType> {
|
||||
|
||||
protected components: Array<IVisualComponent<ComponentsRenderOptions>> = [];
|
||||
protected components: IVisualComponent<ComponentsRenderOptions>[] = [];
|
||||
|
||||
public clear(components: Array<IVisualComponent<ComponentsRenderOptions>> = this.components): void {
|
||||
public getComponents(): IVisualComponent<any>[] {
|
||||
return this.components;
|
||||
}
|
||||
|
||||
public clear(components: IVisualComponent<ComponentsRenderOptions>[] = this.components): void {
|
||||
this.forEach(
|
||||
components,
|
||||
(component: IVisualComponent<ComponentsRenderOptions>) => {
|
||||
|
@ -43,7 +47,7 @@ export abstract class BaseContainerComponent<ConstructorOptionsType, RenderOptio
|
|||
super.clear();
|
||||
}
|
||||
|
||||
public destroy(components: Array<IVisualComponent<ComponentsRenderOptions>> = this.components): void {
|
||||
public destroy(components: IVisualComponent<ComponentsRenderOptions>[] = this.components): void {
|
||||
this.forEach(
|
||||
components.splice(0, components.length),
|
||||
(component: IVisualComponent<any>) => {
|
||||
|
@ -55,7 +59,7 @@ export abstract class BaseContainerComponent<ConstructorOptionsType, RenderOptio
|
|||
}
|
||||
|
||||
protected forEach<ForEachComponentsRenderOptions>(
|
||||
components: Array<IVisualComponent<ForEachComponentsRenderOptions>>,
|
||||
components: IVisualComponent<ForEachComponentsRenderOptions>[],
|
||||
iterator: (
|
||||
component: IVisualComponent<ForEachComponentsRenderOptions>,
|
||||
index: number,
|
||||
|
@ -69,7 +73,7 @@ export abstract class BaseContainerComponent<ConstructorOptionsType, RenderOptio
|
|||
}
|
||||
|
||||
protected initComponents<InitComponentsRenderOptions>(
|
||||
components: Array<IVisualComponent<InitComponentsRenderOptions>>,
|
||||
components: IVisualComponent<InitComponentsRenderOptions>[],
|
||||
expectedAmountOfComponents: number,
|
||||
initComponent: (index: number) => IVisualComponent<InitComponentsRenderOptions>,
|
||||
): void {
|
||||
|
|
|
@ -28,12 +28,11 @@ import { axisRight } from "d3-axis";
|
|||
import { ScaleLinear } from "d3-scale";
|
||||
import { select as d3Select } from "d3-selection";
|
||||
|
||||
import powerbi from "powerbi-visuals-api";
|
||||
import powerbiVisualsApi from "powerbi-visuals-api";
|
||||
|
||||
import { valueFormatter } from "powerbi-visuals-utils-formattingutils";
|
||||
|
||||
import {
|
||||
DataRepresentationAxisValueType,
|
||||
IDataRepresentationAxis,
|
||||
IDataRepresentationSeries,
|
||||
} from "../../converter/data/dataRepresentation";
|
||||
|
@ -44,9 +43,10 @@ import { AxisDescriptor } from "../../settings/descriptors/axisDescriptor";
|
|||
|
||||
import { BaseComponent } from "../baseComponent";
|
||||
import { IVisualComponentConstructorOptions } from "../visualComponentConstructorOptions";
|
||||
import { detectPrecision } from "../../converter/data/dataFormatter";
|
||||
|
||||
export interface IAxisComponentRenderOptions {
|
||||
viewport: powerbi.IViewport;
|
||||
viewport: powerbiVisualsApi.IViewport;
|
||||
settings: AxisDescriptor;
|
||||
series: IDataRepresentationSeries;
|
||||
y: IDataRepresentationAxis;
|
||||
|
@ -90,16 +90,16 @@ export class AxisComponent extends BaseComponent<IVisualComponentConstructorOpti
|
|||
.copy()
|
||||
.range([viewport.height, 0]);
|
||||
|
||||
const domain: number[] = yScale.getDomain() as number[];
|
||||
const domain: number[] = <number[]>(yScale.getDomain());
|
||||
|
||||
const axisValueFormatter: valueFormatter.IValueFormatter = valueFormatter.create({
|
||||
displayUnitSystemType: 2,
|
||||
format: settings.getFormat(),
|
||||
precision: settings.precision,
|
||||
precision: detectPrecision(domain[1] || domain[0], settings),
|
||||
value: settings.displayUnits || domain[1] || domain[0],
|
||||
});
|
||||
|
||||
const yAxis = axisRight(yScale.getScale() as ScaleLinear<number, number>) // TODO: This is a place for potential issue
|
||||
const yAxis = axisRight(<ScaleLinear<number, number>>(yScale.getScale()))
|
||||
.tickValues(domain)
|
||||
.tickFormat((value: number) => {
|
||||
return axisValueFormatter.format(value);
|
||||
|
|
|
@ -28,7 +28,7 @@ import { bisector } from "d3-array";
|
|||
import { Selection } from "d3-selection";
|
||||
import { line } from "d3-shape";
|
||||
|
||||
import powerbi from "powerbi-visuals-api";
|
||||
import powerbiVisualsApi from "powerbi-visuals-api";
|
||||
|
||||
import { IMargin } from "powerbi-visuals-utils-svgutils";
|
||||
|
||||
|
@ -36,6 +36,7 @@ import {
|
|||
IDataRepresentationPoint,
|
||||
IDataRepresentationSeries,
|
||||
ViewportSize,
|
||||
DataRepresentationPointGradientType,
|
||||
} from "../../converter/data/dataRepresentation";
|
||||
|
||||
import { DataRepresentationScale } from "../../converter/data/dataRepresentationScale";
|
||||
|
@ -71,12 +72,12 @@ import { HoverLabelComponent } from "./hoverLabelComponent";
|
|||
export interface IZeroLineRenderOptions {
|
||||
series: IDataRepresentationSeries;
|
||||
chart: ChartDescriptor;
|
||||
viewport: powerbi.IViewport;
|
||||
viewport: powerbiVisualsApi.IViewport;
|
||||
}
|
||||
|
||||
export interface IChartComponentRenderOptions {
|
||||
series: IDataRepresentationSeries;
|
||||
viewport: powerbi.IViewport;
|
||||
viewport: powerbiVisualsApi.IViewport;
|
||||
settings: Settings;
|
||||
viewportSize: ViewportSize;
|
||||
}
|
||||
|
@ -99,7 +100,7 @@ export class ChartComponent extends BaseContainerComponent<
|
|||
private axisComponent: IVisualComponent<IAxisComponentRenderOptions>;
|
||||
private lineComponent: IVisualComponent<ILineComponentRenderOptions>;
|
||||
|
||||
private dynamicComponents: Array<IVisualComponent<IHoverLabelComponentRenderOptions>> = [];
|
||||
private dynamicComponents: IVisualComponent<IHoverLabelComponentRenderOptions>[] = [];
|
||||
|
||||
constructor(options: IVisualComponentConstructorOptions) {
|
||||
super();
|
||||
|
@ -204,9 +205,10 @@ export class ChartComponent extends BaseContainerComponent<
|
|||
alternativeColor: settings.chart.alternativeColor,
|
||||
color: settings.chart.color,
|
||||
current: series.current,
|
||||
isLine: series.isLine,
|
||||
points: series.points,
|
||||
thickness: settings.chart.thickness,
|
||||
type: settings.chart.chartType,
|
||||
type: series.isLine ? DataRepresentationPointGradientType.line : settings.chart.chartType,
|
||||
viewport,
|
||||
x: series.x,
|
||||
y: series.y,
|
||||
|
@ -251,7 +253,7 @@ export class ChartComponent extends BaseContainerComponent<
|
|||
return NaN;
|
||||
}
|
||||
|
||||
const areaScale: powerbi.IViewport = this.constructorOptions.scaleService.getScale();
|
||||
const areaScale: powerbiVisualsApi.IViewport = this.constructorOptions.scaleService.getScale();
|
||||
|
||||
const width: number = this.width;
|
||||
|
||||
|
@ -263,7 +265,7 @@ export class ChartComponent extends BaseContainerComponent<
|
|||
|
||||
const leftPosition: number = (position - thickness) / areaScale.width;
|
||||
|
||||
const x: Date = scale.invert(leftPosition) as Date;
|
||||
const x: Date = <Date>(scale.invert(leftPosition));
|
||||
|
||||
return this.dataBisector(this.renderOptions.series.points, x, 1);
|
||||
}
|
||||
|
|
|
@ -24,7 +24,7 @@
|
|||
* THE SOFTWARE.
|
||||
*/
|
||||
|
||||
import powerbi from "powerbi-visuals-api";
|
||||
import powerbiVisualsApi from "powerbi-visuals-api";
|
||||
|
||||
import { Selection } from "d3-selection";
|
||||
|
||||
|
@ -34,7 +34,7 @@ import { pixelConverter } from "powerbi-visuals-utils-typeutils";
|
|||
import { BaseComponent } from "../baseComponent";
|
||||
import { IVisualComponentConstructorOptions } from "../visualComponentConstructorOptions";
|
||||
|
||||
import VisualTooltipDataItem = powerbi.extensibility.VisualTooltipDataItem;
|
||||
import VisualTooltipDataItem = powerbiVisualsApi.extensibility.VisualTooltipDataItem;
|
||||
|
||||
export interface IRenderGroup {
|
||||
data: string;
|
||||
|
@ -126,10 +126,7 @@ export abstract class ChartLabelBaseComponent<RenderOptions> extends BaseCompone
|
|||
|
||||
this.constructorOptions.tooltipServiceWrapper.addTooltip<IRenderGroup>(
|
||||
itemSelection,
|
||||
(args) => {
|
||||
if (args.data.tooltipDataItems) {
|
||||
return args.data.tooltipDataItems;
|
||||
}
|
||||
});
|
||||
(data) => data.tooltipDataItems ? data.tooltipDataItems : null
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -24,7 +24,7 @@
|
|||
* THE SOFTWARE.
|
||||
*/
|
||||
|
||||
import powerbi from "powerbi-visuals-api";
|
||||
import powerbiVisualsApi from "powerbi-visuals-api";
|
||||
|
||||
import { FormatDescriptor } from "../../settings/descriptors/formatDescriptor";
|
||||
import { KpiDescriptor } from "../../settings/descriptors/kpi/kpiDescriptor";
|
||||
|
@ -37,13 +37,13 @@ import { ChartLabelBaseComponent } from "./chartLabelBaseComponent";
|
|||
|
||||
import { getFormattedValueWithFallback } from "../../converter/data/dataFormatter";
|
||||
import { EventName } from "../../event/eventName";
|
||||
import { isValueValid } from "../../utils/valueUtils";
|
||||
import { isValueValid } from "../../utils/isValueValid";
|
||||
|
||||
export interface IChartLabelComponentRenderOptions {
|
||||
dateSettings: FormatDescriptor;
|
||||
kpiSettings: KpiDescriptor;
|
||||
series: IDataRepresentationSeries;
|
||||
viewport: powerbi.IViewport;
|
||||
viewport: powerbiVisualsApi.IViewport;
|
||||
}
|
||||
|
||||
export class ChartLabelComponent extends ChartLabelBaseComponent<IChartLabelComponentRenderOptions> {
|
||||
|
|
|
@ -24,11 +24,11 @@
|
|||
* THE SOFTWARE.
|
||||
*/
|
||||
|
||||
import powerbi from "powerbi-visuals-api";
|
||||
import powerbiVisualsApi from "powerbi-visuals-api";
|
||||
|
||||
import { ChartLabelBaseComponent, IRenderGroup } from "./chartLabelBaseComponent";
|
||||
|
||||
import { createVarianceConverterByType } from "../../converter/variance";
|
||||
import { createVarianceConverterByType } from "../../converter/variance/createVarianceConverterByType";
|
||||
|
||||
import { IDataRepresentationPoint } from "../../converter/data/dataRepresentation";
|
||||
|
||||
|
@ -37,7 +37,7 @@ import {
|
|||
getFormattedValueWithFallback,
|
||||
} from "../../converter/data/dataFormatter";
|
||||
|
||||
import { isValueValid } from "../../utils/valueUtils";
|
||||
import { isValueValid } from "../../utils/isValueValid";
|
||||
|
||||
import { IVerticalReferenceLineComponentRenderOptions } from "../verticalReferenceLineComponent";
|
||||
import { IVisualComponentConstructorOptions } from "../visualComponentConstructorOptions";
|
||||
|
@ -45,7 +45,7 @@ import { IVisualComponentConstructorOptions } from "../visualComponentConstructo
|
|||
import { KpiOnHoverDescriptor } from "../../settings/descriptors/kpi/kpiOnHoverDescriptor";
|
||||
import { VarianceDescriptor } from "../../settings/descriptors/varianceDescriptor";
|
||||
|
||||
import VisualTooltipDataItem = powerbi.extensibility.VisualTooltipDataItem;
|
||||
import VisualTooltipDataItem = powerbiVisualsApi.extensibility.VisualTooltipDataItem;
|
||||
|
||||
export interface IHoverLabelComponentRenderOptions extends IVerticalReferenceLineComponentRenderOptions {
|
||||
kpiOnHoverSettings: KpiOnHoverDescriptor;
|
||||
|
@ -75,14 +75,12 @@ export class HoverLabelComponent extends ChartLabelBaseComponent<IHoverLabelComp
|
|||
|
||||
if (!series || !series.points || !dataPoint || !series.points.length) {
|
||||
this.hide();
|
||||
|
||||
return;
|
||||
} else {
|
||||
this.show();
|
||||
}
|
||||
|
||||
const latestDataPoint: IDataRepresentationPoint = series.current;
|
||||
|
||||
const variance: number = createVarianceConverterByType(varianceSettings.shouldCalculateDifference)
|
||||
.convert({
|
||||
firstDataPoint: dataPoint,
|
||||
|
@ -103,7 +101,6 @@ export class HoverLabelComponent extends ChartLabelBaseComponent<IHoverLabelComp
|
|||
);
|
||||
|
||||
const isVarianceValid: boolean = isValueValid(variance);
|
||||
|
||||
const tooltipText: string = series && series.formattedTooltip || null;
|
||||
let tooltipDataItems: VisualTooltipDataItem[];
|
||||
|
||||
|
|
|
@ -25,7 +25,7 @@
|
|||
*/
|
||||
|
||||
import { Dispatch } from "d3-dispatch";
|
||||
import { mouse as d3Mouse } from "d3-selection";
|
||||
import { pointer as d3Mouse } from "d3-selection";
|
||||
|
||||
import { BaseContainerComponent } from "../baseContainerComponent";
|
||||
import { IVisualComponentConstructorOptions } from "../visualComponentConstructorOptions";
|
||||
|
@ -98,23 +98,18 @@ export class MainChartComponent extends BaseContainerComponent<IVisualComponentC
|
|||
private initMouseEvents(): void {
|
||||
const eventDispatcher: Dispatch<any> = this.constructorOptions.eventDispatcher;
|
||||
|
||||
function onMouseMove(e: any) {
|
||||
const event: MouseEvent = require("d3").event;
|
||||
|
||||
function onMouseMove(event) {
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
event.stopImmediatePropagation();
|
||||
|
||||
eventDispatcher.call(EventName.onMouseMove, undefined, d3Mouse(this));
|
||||
eventDispatcher.call(EventName.onMouseMove, undefined, d3Mouse(event, this));
|
||||
}
|
||||
|
||||
this.element.on("mousemove", onMouseMove);
|
||||
this.element.on("touchmove", onMouseMove);
|
||||
this.element.on("touchstart", onMouseMove);
|
||||
|
||||
function onMouseOut(e: any) {
|
||||
const event: MouseEvent = require("d3").event;
|
||||
|
||||
function onMouseOut(event) {
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
event.stopImmediatePropagation();
|
||||
|
|
|
@ -24,9 +24,9 @@
|
|||
* THE SOFTWARE.
|
||||
*/
|
||||
|
||||
import powerbi from "powerbi-visuals-api";
|
||||
import powerbiVisualsApi from "powerbi-visuals-api";
|
||||
|
||||
import { mouse as d3Mouse } from "d3-selection";
|
||||
import { pointer as d3Mouse } from "d3-selection";
|
||||
|
||||
import { BaseContainerComponent } from "./baseContainerComponent";
|
||||
|
||||
|
@ -55,12 +55,12 @@ export class RootComponent extends BaseContainerComponent<IVisualComponentConstr
|
|||
|
||||
private isPrintModeActivated: boolean = false;
|
||||
|
||||
private mainChartComponentViewport: powerbi.IViewport;
|
||||
private mainChartComponentViewport: powerbiVisualsApi.IViewport;
|
||||
|
||||
private onChartChangeDelay: number = 300;
|
||||
private onChartChangeTimer: number = null;
|
||||
private currentChartName: string = undefined;
|
||||
private currentlyHoveringChartName: string = undefined;
|
||||
private currentChartName: string;
|
||||
private currentlyHoveringChartName: string;
|
||||
private startCoordinates: [number, number];
|
||||
|
||||
constructor(options: IVisualComponentConstructorOptions) {
|
||||
|
@ -153,9 +153,7 @@ export class RootComponent extends BaseContainerComponent<IVisualComponentConstr
|
|||
this.subtitleComponent = null;
|
||||
}
|
||||
|
||||
private mousemoveHandler(): void {
|
||||
const event: MouseEvent = require("d3").event;
|
||||
|
||||
private mousemoveHandler(event): void {
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
event.stopImmediatePropagation();
|
||||
|
@ -164,22 +162,24 @@ export class RootComponent extends BaseContainerComponent<IVisualComponentConstr
|
|||
return;
|
||||
}
|
||||
|
||||
const coordinates: [number, number] = d3Mouse(this.element.node()) as [number, number];
|
||||
const coordinates: [number, number] = <[number, number]>d3Mouse(event, this.element.node());
|
||||
const delay: number = this.getOnChartChangeDelay(coordinates);
|
||||
|
||||
if (delay) {
|
||||
this.clearOnChartChangeTimer();
|
||||
|
||||
this.onChartChangeTimer = setTimeout(
|
||||
this.onChartChangeTimer = <number>(<unknown>setTimeout(
|
||||
this.applyCurrentlyHoveringChartName.bind(
|
||||
this,
|
||||
event,
|
||||
this.currentlyHoveringChartName,
|
||||
coordinates,
|
||||
),
|
||||
delay,
|
||||
) as unknown as number;
|
||||
));
|
||||
} else {
|
||||
this.applyCurrentlyHoveringChartName(
|
||||
event,
|
||||
this.currentlyHoveringChartName,
|
||||
coordinates,
|
||||
);
|
||||
|
@ -187,7 +187,7 @@ export class RootComponent extends BaseContainerComponent<IVisualComponentConstr
|
|||
}
|
||||
|
||||
private getOnChartChangeDelay([x, y]): number {
|
||||
const scale: powerbi.IViewport = this.constructorOptions.scaleService.getScale();
|
||||
const scale: powerbiVisualsApi.IViewport = this.constructorOptions.scaleService.getScale();
|
||||
|
||||
const scaledX: number = x / scale.width;
|
||||
const scaledY: number = y / scale.height;
|
||||
|
@ -215,13 +215,14 @@ export class RootComponent extends BaseContainerComponent<IVisualComponentConstr
|
|||
staleDataSettings: settings.staleData,
|
||||
subtitleSettings: settings.subtitle,
|
||||
warningState: data.warningState,
|
||||
subtitle: data.subtitle,
|
||||
});
|
||||
|
||||
const subtitleComponentHeight: number = this.subtitleComponent.getViewport().height;
|
||||
|
||||
const viewportFactor: number = this.getViewportFactorByViewportSize(data.viewportSize);
|
||||
|
||||
const viewport: powerbi.IViewport = {
|
||||
const viewport: powerbiVisualsApi.IViewport = {
|
||||
height: options.viewport.height / viewportFactor,
|
||||
width: options.viewport.width,
|
||||
};
|
||||
|
@ -298,8 +299,8 @@ export class RootComponent extends BaseContainerComponent<IVisualComponentConstr
|
|||
try {
|
||||
if (!window
|
||||
|| !window.addEventListener
|
||||
|| !("onbeforeprint" in window as any)
|
||||
|| !("onafterprint" in window as any)
|
||||
|| !("onbeforeprint" in <any>window)
|
||||
|| !("onafterprint" in <any>window)
|
||||
) {
|
||||
return;
|
||||
}
|
||||
|
@ -337,6 +338,7 @@ export class RootComponent extends BaseContainerComponent<IVisualComponentConstr
|
|||
}
|
||||
|
||||
private onChartChangeHoverHandler(
|
||||
event,
|
||||
seriesName: string,
|
||||
coordinates: number[],
|
||||
): void {
|
||||
|
@ -353,9 +355,9 @@ export class RootComponent extends BaseContainerComponent<IVisualComponentConstr
|
|||
this.currentChartName = seriesName;
|
||||
this.currentlyHoveringChartName = undefined;
|
||||
|
||||
this.startCoordinates = (coordinates
|
||||
this.startCoordinates = <[number, number]>(coordinates
|
||||
? coordinates
|
||||
: d3Mouse(this.element.node())) as [number, number];
|
||||
: d3Mouse(event, this.element.node()));
|
||||
|
||||
if (!this.constructorOptions
|
||||
|| !this.constructorOptions.eventDispatcher
|
||||
|
@ -379,22 +381,21 @@ export class RootComponent extends BaseContainerComponent<IVisualComponentConstr
|
|||
this.onChartChangeTimer = null;
|
||||
}
|
||||
|
||||
private applyCurrentlyHoveringChartName(currentlyHoveringChartName: string, positions: number[]): void {
|
||||
private applyCurrentlyHoveringChartName(event, currentlyHoveringChartName: string, positions: number[]): void {
|
||||
this.clearOnChartChangeTimer();
|
||||
|
||||
if (currentlyHoveringChartName) {
|
||||
this.currentChartName = undefined;
|
||||
|
||||
this.onChartChangeHoverHandler(
|
||||
event,
|
||||
currentlyHoveringChartName,
|
||||
positions as [number, number],
|
||||
<[number, number]>positions,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
private onChartViewReset(): void {
|
||||
const event: MouseEvent = require("d3").event;
|
||||
|
||||
private onChartViewReset(event): void {
|
||||
if (event.target !== this.element.node()) {
|
||||
return;
|
||||
}
|
||||
|
|
|
@ -26,7 +26,7 @@
|
|||
|
||||
import { Selection } from "d3-selection";
|
||||
|
||||
import powerbi from "powerbi-visuals-api";
|
||||
import powerbiVisualsApi from "powerbi-visuals-api";
|
||||
import { CssConstants } from "powerbi-visuals-utils-svgutils";
|
||||
|
||||
import {
|
||||
|
@ -41,10 +41,10 @@ import { IVisualComponentConstructorOptions } from "../visualComponentConstructo
|
|||
|
||||
import { DataRepresentationScale } from "../../converter/data/dataRepresentationScale";
|
||||
|
||||
import { isValueValid } from "../../utils/valueUtils";
|
||||
import { isValueValid } from "../../utils/isValueValid";
|
||||
|
||||
export interface IDotsComponentRenderOptions {
|
||||
viewport: powerbi.IViewport;
|
||||
viewport: powerbiVisualsApi.IViewport;
|
||||
x: IDataRepresentationAxis;
|
||||
y: IDataRepresentationAxis;
|
||||
points: IDataRepresentationPoint[];
|
||||
|
|
|
@ -26,7 +26,7 @@
|
|||
|
||||
import { Selection } from "d3-selection";
|
||||
import { area, Area, line, Line } from "d3-shape";
|
||||
import powerbi from "powerbi-visuals-api";
|
||||
import powerbiVisualsApi from "powerbi-visuals-api";
|
||||
import { CssConstants } from "powerbi-visuals-utils-svgutils";
|
||||
import { pixelConverter } from "powerbi-visuals-utils-typeutils";
|
||||
import {
|
||||
|
@ -37,17 +37,18 @@ import {
|
|||
} from "../../converter/data/dataRepresentation";
|
||||
import { DataRepresentationScale } from "../../converter/data/dataRepresentationScale";
|
||||
import { EventName } from "../../event/eventName";
|
||||
import { isValueValid } from "../../utils/valueUtils";
|
||||
import { isValueValid } from "../../utils/isValueValid";
|
||||
import { BaseComponent } from "../baseComponent";
|
||||
import { IVisualComponentConstructorOptions } from "../visualComponentConstructorOptions";
|
||||
|
||||
export interface ILineComponentRenderOptions {
|
||||
alternativeColor: string;
|
||||
color: string;
|
||||
isLine: boolean;
|
||||
points: IDataRepresentationPoint[];
|
||||
thickness: number;
|
||||
type: DataRepresentationPointGradientType;
|
||||
viewport: powerbi.IViewport;
|
||||
viewport: powerbiVisualsApi.IViewport;
|
||||
x: IDataRepresentationAxis;
|
||||
y: IDataRepresentationAxis;
|
||||
current: IDataRepresentationPoint;
|
||||
|
@ -189,6 +190,7 @@ export class LineComponent extends BaseComponent<IVisualComponentConstructorOpti
|
|||
y,
|
||||
viewport,
|
||||
color,
|
||||
isLine,
|
||||
} = options;
|
||||
|
||||
this.updateGradient([{
|
||||
|
@ -219,6 +221,10 @@ export class LineComponent extends BaseComponent<IVisualComponentConstructorOpti
|
|||
.attr("d", (lineRenderOptions: ILineComponentRenderOptions) => {
|
||||
const points: IDataRepresentationPoint[] = this.getValidPoints(lineRenderOptions.points);
|
||||
|
||||
if (isLine && points.length > 0) {
|
||||
points[0].y += 0.0000001;
|
||||
}
|
||||
|
||||
switch (lineRenderOptions.type) {
|
||||
case DataRepresentationPointGradientType.area: {
|
||||
return this.getArea(xScale, yScale, viewport, y.min)(points);
|
||||
|
@ -258,7 +264,8 @@ export class LineComponent extends BaseComponent<IVisualComponentConstructorOpti
|
|||
}
|
||||
case DataRepresentationPointGradientType.line:
|
||||
default: {
|
||||
return pixelConverter.toString(lineRenderOptions.thickness);
|
||||
//return lineRenderOptions.thickness;
|
||||
pixelConverter.toString(lineRenderOptions.thickness);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
@ -280,7 +287,7 @@ export class LineComponent extends BaseComponent<IVisualComponentConstructorOpti
|
|||
private getArea(
|
||||
xScale: DataRepresentationScale,
|
||||
yScale: DataRepresentationScale,
|
||||
viewport: powerbi.IViewport,
|
||||
viewport: powerbiVisualsApi.IViewport,
|
||||
yMin: DataRepresentationAxisValueType,
|
||||
): Area<IDataRepresentationPoint> {
|
||||
return area<IDataRepresentationPoint>()
|
||||
|
@ -302,7 +309,7 @@ export class LineComponent extends BaseComponent<IVisualComponentConstructorOpti
|
|||
private getGradientUrl(): string {
|
||||
const href: string = window.location.href;
|
||||
|
||||
return `url(${href}#${this.gradientId})`;
|
||||
return `url(#${this.gradientId})`;
|
||||
}
|
||||
|
||||
private updateGradient(gradients: ILineComponentGradient[]): void {
|
||||
|
@ -328,7 +335,7 @@ export class LineComponent extends BaseComponent<IVisualComponentConstructorOpti
|
|||
}
|
||||
|
||||
private getId(): string {
|
||||
const crypto: Crypto = window.crypto || (window as any).msCrypto;
|
||||
const crypto: Crypto = window.crypto || (<any>window).msCrypto;
|
||||
const generatedIds: Uint32Array = new Uint32Array(2);
|
||||
|
||||
crypto.getRandomValues(generatedIds);
|
||||
|
|
|
@ -82,6 +82,7 @@ export class MultiLineComponent
|
|||
alternativeColor: currentSeries.settings.sparklineChart.alternativeColor,
|
||||
color: currentSeries.settings.sparklineChart.color,
|
||||
current: currentSeries.current,
|
||||
isLine: currentSeries.isLine,
|
||||
points: currentSeries.smoothedPoints,
|
||||
thickness: currentSeries.settings.sparklineChart.thickness,
|
||||
type: DataRepresentationPointGradientType.line,
|
||||
|
|
|
@ -24,7 +24,7 @@
|
|||
* THE SOFTWARE.
|
||||
*/
|
||||
|
||||
import powerbi from "powerbi-visuals-api";
|
||||
import powerbiVisualsApi from "powerbi-visuals-api";
|
||||
|
||||
import { BaseContainerComponent } from "../baseContainerComponent";
|
||||
import { ISparklineComponentRenderOptions } from "./sparklineComponent";
|
||||
|
@ -50,7 +50,6 @@ export class PlotComponent
|
|||
};
|
||||
|
||||
this.components = [
|
||||
// TODO: Axis Component must be included here
|
||||
new SvgComponent(this.constructorOptions),
|
||||
];
|
||||
}
|
||||
|
@ -60,7 +59,7 @@ export class PlotComponent
|
|||
|
||||
this.updateSize(viewport.width, viewport.height);
|
||||
|
||||
const componentViewport: powerbi.IViewport = { ...viewport };
|
||||
const componentViewport: powerbiVisualsApi.IViewport = { ...viewport };
|
||||
|
||||
this.forEach(
|
||||
this.components,
|
||||
|
@ -70,7 +69,7 @@ export class PlotComponent
|
|||
viewport: { ...componentViewport },
|
||||
});
|
||||
|
||||
const margins: powerbi.IViewport = component.getViewport();
|
||||
const margins: powerbiVisualsApi.IViewport = component.getViewport();
|
||||
|
||||
componentViewport.height -= margins.height;
|
||||
},
|
||||
|
|
|
@ -24,7 +24,7 @@
|
|||
* THE SOFTWARE.
|
||||
*/
|
||||
|
||||
import powerbi from "powerbi-visuals-api";
|
||||
import powerbiVisualsApi from "powerbi-visuals-api";
|
||||
import { getFormattedValueWithFallback } from "../../converter/data/dataFormatter";
|
||||
import { IDataRepresentation, IDataRepresentationPoint, IDataRepresentationSeries, ViewportSize } from "../../converter/data/dataRepresentation";
|
||||
import { EventName } from "../../event/eventName";
|
||||
|
@ -40,7 +40,7 @@ export interface ISparklineComponentRenderOptions {
|
|||
series: IDataRepresentationSeries[];
|
||||
current: IDataRepresentationSeries;
|
||||
dataRepresentation: IDataRepresentation;
|
||||
viewport: powerbi.IViewport;
|
||||
viewport: powerbiVisualsApi.IViewport;
|
||||
position: number;
|
||||
}
|
||||
|
||||
|
@ -64,9 +64,7 @@ export class SparklineComponent extends BaseContainerComponent<IVisualComponentC
|
|||
element: this.element,
|
||||
};
|
||||
|
||||
this.element.on("click", () => {
|
||||
const event: MouseEvent = require("d3").event;
|
||||
|
||||
this.element.on("click", (event) => {
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
event.stopImmediatePropagation();
|
||||
|
@ -125,14 +123,8 @@ export class SparklineComponent extends BaseContainerComponent<IVisualComponentC
|
|||
|
||||
this.constructorOptions.tooltipServiceWrapper.addTooltip(
|
||||
this.element,
|
||||
() => {
|
||||
if (tooltipText) {
|
||||
return [{
|
||||
displayName: null,
|
||||
value: tooltipText,
|
||||
}];
|
||||
}
|
||||
});
|
||||
(data) => tooltipText ? [{ displayName: null, value: tooltipText, }] : null
|
||||
);
|
||||
|
||||
} else {
|
||||
this.destroyComponents();
|
||||
|
@ -222,7 +214,7 @@ export class SparklineComponent extends BaseContainerComponent<IVisualComponentC
|
|||
}
|
||||
|
||||
private renderPlot(options: ISparklineComponentRenderOptions): void {
|
||||
const plotComponentViewport: powerbi.IViewport = this.getReducedViewport(
|
||||
const plotComponentViewport: powerbiVisualsApi.IViewport = this.getReducedViewport(
|
||||
{ ...options.viewport },
|
||||
[this.topLabelComponent, this.bottomLabelComponent],
|
||||
);
|
||||
|
@ -269,12 +261,12 @@ export class SparklineComponent extends BaseContainerComponent<IVisualComponentC
|
|||
}
|
||||
}
|
||||
|
||||
private getReducedViewport(viewport: powerbi.IViewport, components: Array<IVisualComponent<any>>): powerbi.IViewport {
|
||||
return components.reduce<powerbi.IViewport>((
|
||||
previousViewport: powerbi.IViewport,
|
||||
private getReducedViewport(viewport: powerbiVisualsApi.IViewport, components: IVisualComponent<any>[]): powerbiVisualsApi.IViewport {
|
||||
return components.reduce<powerbiVisualsApi.IViewport>((
|
||||
previousViewport: powerbiVisualsApi.IViewport,
|
||||
component: IVisualComponent<any>,
|
||||
): powerbi.IViewport => {
|
||||
const componentViewport: powerbi.IViewport = component.getViewport();
|
||||
): powerbiVisualsApi.IViewport => {
|
||||
const componentViewport: powerbiVisualsApi.IViewport = component.getViewport();
|
||||
|
||||
return {
|
||||
height: previousViewport.height - componentViewport.height,
|
||||
|
|
|
@ -139,9 +139,9 @@ export class SparklineGroupComponent
|
|||
clearTimeout(this.renderingTimers[index]);
|
||||
}
|
||||
|
||||
this.renderingTimers[index] = setTimeout(
|
||||
this.renderingTimers[index] = <number>(<unknown>setTimeout(
|
||||
component.render.bind(component, options),
|
||||
this.renderingDelay,
|
||||
) as unknown as number;
|
||||
));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -24,7 +24,7 @@
|
|||
* THE SOFTWARE.
|
||||
*/
|
||||
|
||||
import powerbi from "powerbi-visuals-api";
|
||||
import powerbiVisualsApi from "powerbi-visuals-api";
|
||||
import { IMargin } from "powerbi-visuals-utils-svgutils";
|
||||
|
||||
import {
|
||||
|
@ -51,7 +51,7 @@ import {
|
|||
import { EventName } from "../../event/eventName";
|
||||
import { MultiLineComponent } from "./multiLineComponent";
|
||||
|
||||
import { isValueValid } from "../../utils/valueUtils";
|
||||
import { isValueValid } from "../../utils/isValueValid";
|
||||
|
||||
export class SvgComponent extends BaseContainerComponent<
|
||||
IVisualComponentConstructorOptions,
|
||||
|
@ -63,7 +63,7 @@ export class SvgComponent extends BaseContainerComponent<
|
|||
private multiLineComponent: IVisualComponent<ISparklineComponentRenderOptions>;
|
||||
private axisComponent: IVisualComponent<IAxisComponentRenderOptions>;
|
||||
|
||||
private dynamicComponents: Array<IVisualComponent<IDotsComponentRenderOptions>> = [];
|
||||
private dynamicComponents: IVisualComponent<IDotsComponentRenderOptions>[] = [];
|
||||
|
||||
constructor(options: IVisualComponentConstructorOptions) {
|
||||
super();
|
||||
|
@ -119,7 +119,7 @@ export class SvgComponent extends BaseContainerComponent<
|
|||
|
||||
const margin: IMargin = this.getMarginByThickness(maxThickness);
|
||||
|
||||
const viewport: powerbi.IViewport = {
|
||||
const viewport: powerbiVisualsApi.IViewport = {
|
||||
height: Math.max(0, options.viewport.height - margin.top - margin.bottom),
|
||||
width: Math.max(0, options.viewport.width - margin.left - margin.right),
|
||||
};
|
||||
|
|
|
@ -26,7 +26,7 @@
|
|||
|
||||
import { Selection } from "d3-selection";
|
||||
|
||||
import powerbi from "powerbi-visuals-api";
|
||||
import powerbiVisualsApi from "powerbi-visuals-api";
|
||||
|
||||
import { CssConstants } from "powerbi-visuals-utils-svgutils";
|
||||
import { pixelConverter } from "powerbi-visuals-utils-typeutils";
|
||||
|
@ -38,6 +38,7 @@ import { IVisualComponentConstructorOptions } from "./visualComponentConstructor
|
|||
|
||||
export interface ISubtitleComponentRenderOptions {
|
||||
subtitleSettings: SubtitleDescriptor;
|
||||
subtitle?: string;
|
||||
}
|
||||
|
||||
export class SubtitleComponent extends BaseComponent<IVisualComponentConstructorOptions, ISubtitleComponentRenderOptions> {
|
||||
|
@ -60,19 +61,19 @@ export class SubtitleComponent extends BaseComponent<IVisualComponentConstructor
|
|||
}
|
||||
|
||||
public render(options: ISubtitleComponentRenderOptions): void {
|
||||
const { subtitleSettings: settings } = options;
|
||||
const { subtitleSettings: settings, subtitle } = options;
|
||||
|
||||
if (settings.shouldBeShown) {
|
||||
this.show();
|
||||
this.renderComponent(settings);
|
||||
this.renderComponent(settings, subtitle);
|
||||
} else {
|
||||
this.hide();
|
||||
}
|
||||
}
|
||||
|
||||
public getViewport(): powerbi.IViewport {
|
||||
public getViewport(): powerbiVisualsApi.IViewport {
|
||||
const height: number = this.element && this.isShown
|
||||
? (this.element.node() as HTMLElement).clientHeight
|
||||
? (<HTMLElement>(this.element.node())).clientHeight
|
||||
: 0;
|
||||
|
||||
return {
|
||||
|
@ -81,7 +82,7 @@ export class SubtitleComponent extends BaseComponent<IVisualComponentConstructor
|
|||
};
|
||||
}
|
||||
|
||||
private renderComponent(settings: SubtitleDescriptor): void {
|
||||
private renderComponent(settings: SubtitleDescriptor, subtitle?: string): void {
|
||||
const subtitleSelection: Selection<any, any, any, any> = this.element
|
||||
.selectAll(this.subtitleSelector.selectorName)
|
||||
.data(settings.shouldBeShown ? [[]] : []);
|
||||
|
@ -90,12 +91,14 @@ export class SubtitleComponent extends BaseComponent<IVisualComponentConstructor
|
|||
.exit()
|
||||
.remove();
|
||||
|
||||
const subtitleText: string = `${settings.titleText}${(subtitle ?? "")}`;
|
||||
|
||||
subtitleSelection
|
||||
.enter()
|
||||
.append("div")
|
||||
.classed(this.subtitleSelector.className, true)
|
||||
.merge(subtitleSelection)
|
||||
.text(settings.titleText)
|
||||
.text(subtitleText)
|
||||
.style("text-align", settings.alignment);
|
||||
|
||||
this.updateFormatting(this.element, settings);
|
||||
|
|
|
@ -24,7 +24,7 @@
|
|||
* THE SOFTWARE.
|
||||
*/
|
||||
import { Selection } from "d3-selection";
|
||||
import powerbi from "powerbi-visuals-api";
|
||||
import powerbiVisualsApi from "powerbi-visuals-api";
|
||||
import { CssConstants } from "powerbi-visuals-utils-svgutils";
|
||||
import { IDataRepresentationSeries } from "../converter/data/dataRepresentation";
|
||||
import { StaleDataDescriptor } from "../settings/descriptors/staleDataDescriptor";
|
||||
|
@ -32,7 +32,7 @@ import { SubtitleWarningDescriptor } from "../settings/descriptors/subtitleWarni
|
|||
import { ISubtitleComponentRenderOptions, SubtitleComponent } from "./subtitleComponent";
|
||||
import { IVisualComponentConstructorOptions } from "./visualComponentConstructorOptions";
|
||||
|
||||
import VisualTooltipDataItem = powerbi.extensibility.VisualTooltipDataItem;
|
||||
import VisualTooltipDataItem = powerbiVisualsApi.extensibility.VisualTooltipDataItem;
|
||||
|
||||
export interface ISubtitleWarningComponentRenderOptions extends ISubtitleComponentRenderOptions {
|
||||
warningState: number;
|
||||
|
@ -103,17 +103,17 @@ export class SubtitleWarningComponent extends SubtitleComponent {
|
|||
|
||||
const isDataStale: boolean = this.isDataStale(
|
||||
staleDataDifference,
|
||||
staleDataThreshold,
|
||||
series,
|
||||
);
|
||||
|
||||
let tooltipItems: VisualTooltipDataItem[] = [];
|
||||
|
||||
const filterItemsFunc = (x: IDataRepresentationSeries) => {
|
||||
if (!x.settings.staleData.isShown) {
|
||||
return false;
|
||||
}
|
||||
if (x.staleDateDifference) {
|
||||
if (staleDataSettings.deductThresholdDays) {
|
||||
return (x.staleDateDifference - staleDataThreshold > 0);
|
||||
}
|
||||
return (x.staleDateDifference > 0);
|
||||
return (x.staleDateDifference - x.settings.staleData.staleDataThreshold > 0);
|
||||
}
|
||||
return false;
|
||||
};
|
||||
|
@ -132,17 +132,15 @@ export class SubtitleWarningComponent extends SubtitleComponent {
|
|||
if (!isTheSameStaledays) {
|
||||
tooltipItems = series.filter(filterItemsFunc).map((s) => {
|
||||
const title: string = this.getTitle(
|
||||
staleDataText,
|
||||
s.settings.staleData.staleDataText,
|
||||
s.staleDateDifference,
|
||||
staleDataSettings.deductThresholdDays ? staleDataThreshold : 0,
|
||||
s.settings.staleData.deductThresholdDays ? s.settings.staleData.staleDataThreshold : 0,
|
||||
);
|
||||
|
||||
const tolltipItem: VisualTooltipDataItem = {
|
||||
return {
|
||||
displayName: s.name,
|
||||
value: title,
|
||||
};
|
||||
|
||||
return tolltipItem;
|
||||
});
|
||||
} else {
|
||||
tooltipItems = [
|
||||
|
@ -166,11 +164,17 @@ export class SubtitleWarningComponent extends SubtitleComponent {
|
|||
});
|
||||
}
|
||||
|
||||
private isDataStale(dateDifference: number, staleDataThreshold: number): boolean {
|
||||
return dateDifference > staleDataThreshold;
|
||||
private isDataStale(dateDifference: number, series: IDataRepresentationSeries[]): boolean {
|
||||
let isStale: boolean = false;
|
||||
series.forEach((s) => {
|
||||
if (dateDifference > s.settings.staleData.staleDataThreshold) {
|
||||
isStale = true;
|
||||
}
|
||||
})
|
||||
return isStale;
|
||||
}
|
||||
|
||||
private getTitle(stringTemplate: string, dateDifference: number, staleDataThreshold: number): string {
|
||||
public getTitle(stringTemplate: string, dateDifference: number, staleDataThreshold: number): string {
|
||||
const days: number = dateDifference - staleDataThreshold;
|
||||
return stringTemplate && stringTemplate.replace
|
||||
? stringTemplate.replace("${1}", `${days}`)
|
||||
|
@ -202,10 +206,7 @@ export class SubtitleWarningComponent extends SubtitleComponent {
|
|||
|
||||
this.constructorOptions.tooltipServiceWrapper.addTooltip(
|
||||
iconSelection,
|
||||
() => {
|
||||
if (tooltipItems && tooltipItems.length > 0) {
|
||||
return tooltipItems;
|
||||
}
|
||||
});
|
||||
(data) => tooltipItems ? tooltipItems : null
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -24,7 +24,7 @@
|
|||
* THE SOFTWARE.
|
||||
*/
|
||||
|
||||
import powerbi from "powerbi-visuals-api";
|
||||
import powerbiVisualsApi from "powerbi-visuals-api";
|
||||
|
||||
import { Selection } from "d3-selection";
|
||||
|
||||
|
@ -46,7 +46,7 @@ import { DataRepresentationScale } from "../converter/data/dataRepresentationSca
|
|||
|
||||
export interface IVerticalReferenceLineComponentRenderOptions {
|
||||
offset: number;
|
||||
viewport: powerbi.IViewport;
|
||||
viewport: powerbiVisualsApi.IViewport;
|
||||
series: IDataRepresentationSeries;
|
||||
dataPoint: IDataRepresentationPoint;
|
||||
kpiSettings: KpiDescriptor;
|
||||
|
|
|
@ -24,7 +24,7 @@
|
|||
* THE SOFTWARE.
|
||||
*/
|
||||
|
||||
import powerbi from "powerbi-visuals-api";
|
||||
import powerbiVisualsApi from "powerbi-visuals-api";
|
||||
|
||||
export interface IVisualComponent<RenderOptionsType> {
|
||||
isShown?: boolean;
|
||||
|
@ -35,5 +35,6 @@ export interface IVisualComponent<RenderOptionsType> {
|
|||
hide?(): void;
|
||||
show?(): void;
|
||||
toggle?(): void;
|
||||
getViewport?(): powerbi.IViewport;
|
||||
getViewport?(): powerbiVisualsApi.IViewport;
|
||||
getComponents?(): IVisualComponent<any>[]
|
||||
}
|
||||
|
|
|
@ -25,7 +25,7 @@
|
|||
*/
|
||||
|
||||
import { Dispatch } from "d3-dispatch";
|
||||
import powerbi from "powerbi-visuals-api";
|
||||
import powerbiVisualsApi from "powerbi-visuals-api";
|
||||
|
||||
import { ScaleService } from "../services/scaleService";
|
||||
import { Settings } from "../settings/settings";
|
||||
|
@ -36,7 +36,7 @@ export interface IVisualComponentConstructorOptions extends IVisualComponentBase
|
|||
eventDispatcher?: Dispatch<any>; // TODO
|
||||
id?: number | string;
|
||||
scaleService?: ScaleService;
|
||||
style?: powerbi.extensibility.IColorPalette; // TODO: must be renamed
|
||||
style?: powerbiVisualsApi.extensibility.IColorPalette; // TODO: must be renamed
|
||||
getSettings?: () => Settings;
|
||||
tooltipServiceWrapper?: ITooltipServiceWrapper;
|
||||
}
|
||||
|
|
|
@ -24,13 +24,13 @@
|
|||
* THE SOFTWARE.
|
||||
*/
|
||||
|
||||
import powerbi from "powerbi-visuals-api";
|
||||
import powerbiVisualsApi from "powerbi-visuals-api";
|
||||
|
||||
import { IDataRepresentation } from "../converter/data/dataRepresentation";
|
||||
import { Settings } from "../settings/settings";
|
||||
|
||||
export interface IVisualComponentRenderOptions {
|
||||
settings: Settings;
|
||||
viewport: powerbi.IViewport;
|
||||
viewport: powerbiVisualsApi.IViewport;
|
||||
data: IDataRepresentation;
|
||||
}
|
||||
|
|
|
@ -15,6 +15,6 @@
|
|||
]
|
||||
},
|
||||
"files": [
|
||||
"./src/visual.ts"
|
||||
"src/multiKpi.ts"
|
||||
]
|
||||
}
|
||||
|
|
91
tslint.json
91
tslint.json
|
@ -1,88 +1,9 @@
|
|||
{
|
||||
"extends": "tslint:recommended",
|
||||
"rulesDirectory": ["./node_modules/tslint-microsoft-contrib"],
|
||||
"rules": {
|
||||
"insecure-random": false,
|
||||
"no-banned-terms": true,
|
||||
"no-delete-expression": true,
|
||||
"no-disable-auto-sanitization": true,
|
||||
"no-document-domain": true,
|
||||
"no-document-write": true,
|
||||
"no-exec-script": true,
|
||||
"no-function-constructor-with-string-args": true,
|
||||
"no-http-string": [
|
||||
true
|
||||
"extends": "tslint-microsoft-contrib/recommended",
|
||||
"rulesDirectory": [
|
||||
"node_modules/tslint-microsoft-contrib"
|
||||
],
|
||||
"no-inner-html": true,
|
||||
"no-octal-literal": true,
|
||||
"no-reserved-keywords": true,
|
||||
"no-string-based-set-immediate": true,
|
||||
"no-string-based-set-interval": true,
|
||||
"no-string-based-set-timeout": true,
|
||||
"non-literal-require": true,
|
||||
"possible-timing-attack": true,
|
||||
"react-anchor-blank-noopener": true,
|
||||
"react-iframe-missing-sandbox": true,
|
||||
"react-no-dangerous-html": true,
|
||||
"no-eval": true,
|
||||
"class-name": true,
|
||||
"max-line-length": {
|
||||
"options": [140]
|
||||
},
|
||||
"comment-format": [
|
||||
true,
|
||||
"check-space"
|
||||
],
|
||||
"indent": [
|
||||
true,
|
||||
"spaces"
|
||||
],
|
||||
"no-duplicate-variable": true,
|
||||
"no-internal-module": false,
|
||||
"no-trailing-whitespace": true,
|
||||
"no-unsafe-finally": true,
|
||||
"no-var-keyword": true,
|
||||
"no-unused-expression": true,
|
||||
"one-line": [
|
||||
true,
|
||||
"check-open-brace",
|
||||
"check-whitespace"
|
||||
],
|
||||
"quotemark": [
|
||||
true,
|
||||
"double"
|
||||
],
|
||||
"semicolon": [
|
||||
true,
|
||||
"always"
|
||||
],
|
||||
"triple-equals": [
|
||||
true,
|
||||
"allow-null-check"
|
||||
],
|
||||
"typedef-whitespace": [
|
||||
true,
|
||||
{
|
||||
"call-signature": "nospace",
|
||||
"index-signature": "nospace",
|
||||
"parameter": "nospace",
|
||||
"property-declaration": "nospace",
|
||||
"variable-declaration": "nospace"
|
||||
}
|
||||
],
|
||||
"variable-name": [
|
||||
true,
|
||||
"ban-keywords"
|
||||
],
|
||||
"whitespace": [
|
||||
true,
|
||||
"check-branch",
|
||||
"check-decl",
|
||||
"check-operator",
|
||||
"check-separator",
|
||||
"check-type"
|
||||
],
|
||||
"forin": false,
|
||||
"no-reserved-keywords": false
|
||||
}
|
||||
"rules": {
|
||||
"no-relative-imports": false
|
||||
}
|
||||
}
|
||||
|
|
Загрузка…
Ссылка в новой задаче