diff --git a/package.json b/package.json index b00f0ce..aedd772 100644 --- a/package.json +++ b/package.json @@ -14,7 +14,7 @@ "d3-selection": "^3.0.0", "d3-shape": "^3.2.0", "powerbi-visuals-api": "~3.6.0", - "powerbi-visuals-utils-dataviewutils": "^2.2.1" + "powerbi-visuals-utils-dataviewutils": "^6.0.1" }, "devDependencies": { "@types/d3-array": "^3.0.5", @@ -22,13 +22,12 @@ "@types/d3-scale": "^4.0.3", "@types/d3-selection": "^3.0.5", "@types/d3-shape": "^3.1.1", - "@typescript-eslint/eslint-plugin": "^5.59.8", - "@typescript-eslint/parser": "^5.59.8", - "eslint": "^8.41.0", + "@typescript-eslint/eslint-plugin": "^6.2.0", + "@typescript-eslint/parser": "^6.2.0", + "eslint": "^8.45.0", "eslint-plugin-powerbi-visuals": "^0.8.1", - "process": "^0.11.10", - "ts-loader": "9.4.2", - "typescript": "3.6.3" + "ts-loader": "9.4.4", + "typescript": "5.1.6" }, "override": { "powerbi-visuals-api": "$powerbi-visuals-api" diff --git a/src/Classes/controlLimitsClass.ts b/src/Classes/controlLimitsClass.ts index a040ad0..b5c349e 100644 --- a/src/Classes/controlLimitsClass.ts +++ b/src/Classes/controlLimitsClass.ts @@ -8,13 +8,14 @@ import checkFlagDirection from "../Functions/checkFlagDirection" import truncate from "../Functions/truncate"; import { truncateInputs } from "../Functions/truncate"; import { multiply } from "../Functions/BinaryFunctions"; +import isNotNullOrUndefined from "../Functions/isNotNullOrUndefined"; type controlLimitsArgs = { inputSettings: settingsClass, keys: { x: number, id: number, label: string }[]; values: number[]; - numerators?: number[]; - denominators?: number[]; + numerators?: number[] | undefined; + denominators?: number[] | undefined; targets: number[]; ll99: number[]; ll95: number[]; @@ -30,7 +31,7 @@ export default class controlLimitsClass { numerators?: number[]; denominators?: number[]; targets: number[]; - alt_targets?: number[]; + alt_targets: number[]; ll99: number[]; ll95: number[]; ul95: number[]; @@ -86,30 +87,32 @@ export default class controlLimitsClass { } } - constructor(args: controlLimitsArgs) { - this.keys = args.keys; - this.values = args.values; - if (args.numerators || !(args.numerators === null || args.numerators === undefined)) { - this.numerators = args.numerators; - } - if (args.denominators || !(args.denominators === null || args.denominators === undefined)) { - this.denominators = args.denominators; - } - this.targets = args.targets; - this.alt_targets = rep(args.inputSettings.spc.alt_target, args.values.length) - this.ll99 = args.ll99; - this.ll95 = args.ll95; - this.ul95 = args.ul95; - this.ul99 = args.ul99; - this.astpoint = rep("none", args.values.length); - this.trend = rep("none", args.values.length); - this.two_in_three = rep("none", args.values.length); - this.shift = rep("none", args.values.length); - if (args.count || !(args.count === null || args.count === undefined)) { - this.count = args.count; - } + constructor(args?: controlLimitsArgs) { + if (args) { + this.keys = args.keys; + this.values = args.values; + if (isNotNullOrUndefined(args.numerators)) { + this.numerators = args.numerators; + } + if (isNotNullOrUndefined(args.denominators)) { + this.denominators = args.denominators; + } + this.targets = args.targets; + this.alt_targets = rep(args.inputSettings.spc.alt_target, args.values.length) + this.ll99 = args.ll99; + this.ll95 = args.ll95; + this.ul95 = args.ul95; + this.ul99 = args.ul99; + this.astpoint = rep("none", args.values.length); + this.trend = rep("none", args.values.length); + this.two_in_three = rep("none", args.values.length); + this.shift = rep("none", args.values.length); + if (args.count || !(args.count === null || args.count === undefined)) { + this.count = args.count; + } - this.scaleAndTruncateLimits(args.inputSettings) - this.flagOutliers(args.inputSettings) + this.scaleAndTruncateLimits(args.inputSettings) + this.flagOutliers(args.inputSettings) + } } } diff --git a/src/Classes/dataClass.ts b/src/Classes/dataClass.ts index 1cd7ae1..bd055a9 100644 --- a/src/Classes/dataClass.ts +++ b/src/Classes/dataClass.ts @@ -22,44 +22,48 @@ export default class dataClass { scatter_formatting: defaultSettingsType["scatter"][]; tooltips: VisualTooltipDataItem[][]; - constructor(inputView: DataViewCategorical, inputSettings: settingsClass) { - const numerators: number[] = extractDataColumn(inputView, "numerators", inputSettings); - const denominators: number[] = extractDataColumn(inputView, "denominators", inputSettings); - const xbar_sds: number[] = extractDataColumn(inputView, "xbar_sds", inputSettings); - const keys: string[] = extractDataColumn(inputView, "key", inputSettings); - const scatter_cond = extractConditionalFormatting(inputView, "scatter", inputSettings) as defaultSettingsType["scatter"][]; - const tooltips = extractDataColumn(inputView, "tooltips", inputSettings); + constructor(inputView?: DataViewCategorical, inputSettings?: settingsClass) { + if (inputView && inputSettings) { + const numerators: number[] = extractDataColumn(inputView, "numerators", inputSettings); + const denominators: number[] = extractDataColumn(inputView, "denominators", inputSettings); + const xbar_sds: number[] = extractDataColumn(inputView, "xbar_sds", inputSettings); + const keys: string[] = extractDataColumn(inputView, "key", inputSettings); + const scatter_cond = extractConditionalFormatting(inputView, "scatter", inputSettings) as defaultSettingsType["scatter"][]; + const tooltips = extractDataColumn(inputView, "tooltips", inputSettings); - const valid_ids: number[] = new Array(); - const valid_keys: { x: number, id: number, label: string }[] = new Array<{ x: number, id: number, label: string }>(); + const valid_ids: number[] = new Array(); + const valid_keys: { x: number, id: number, label: string }[] = new Array<{ x: number, id: number, label: string }>(); - for (let i: number = 0; i < numerators.length; i++) { - if (checkValidInput(numerators[i], - denominators ? denominators[i] : null, - xbar_sds ? xbar_sds[i] : null, inputSettings.spc.chart_type)) { - valid_ids.push(i); - valid_keys.push({ x: null, id: i, label: keys[i] }) + let validCount: number = 0 + for (let i: number = 0; i < numerators.length; i++) { + if (checkValidInput(numerators[i], + denominators ? denominators[i] : null, + xbar_sds ? xbar_sds[i] : null, inputSettings.spc.chart_type)) { + valid_ids.push(i); + valid_keys.push({ x: validCount, id: i, label: keys[i] }) + validCount += 1; + } + } + + let percent_labels: boolean; + if (inputSettings.spc.perc_labels === "Automatic") { + percent_labels = ["p", "pp"].includes(inputSettings.spc.chart_type) && (inputSettings.spc.multiplier === 1 || inputSettings.spc.multiplier === 100); + } else { + percent_labels = inputSettings.spc.perc_labels === "Yes"; } - } - valid_keys.forEach((d, idx) => { d.x = idx }); + const inputValues = (inputView.values as powerbi.DataViewValueColumns)[0]; - let percent_labels: boolean; - if (inputSettings.spc.perc_labels === "Automatic") { - percent_labels = ["p", "pp"].includes(inputSettings.spc.chart_type) && (inputSettings.spc.multiplier === 1 || inputSettings.spc.multiplier === 100); - } else { - percent_labels = inputSettings.spc.perc_labels === "Yes"; + this.keys = valid_keys; + this.numerators = extractValues(numerators, valid_ids); + this.denominators = extractValues(denominators, valid_ids); + this.xbar_sds = extractValues(xbar_sds, valid_ids); + this.tooltips = extractValues(tooltips, valid_ids); + this.highlights = inputValues.highlights ? extractValues(inputValues.highlights, valid_ids) : new Array; + this.anyHighlights = (this.highlights.length > 0) + this.categories = (inputView.categories as powerbi.DataViewCategoryColumn[])[0]; + this.percentLabels = percent_labels; + this.scatter_formatting = extractValues(scatter_cond, valid_ids); } - - this.keys = valid_keys; - this.numerators = extractValues(numerators, valid_ids); - this.denominators = extractValues(denominators, valid_ids); - this.xbar_sds = extractValues(xbar_sds, valid_ids); - this.tooltips = extractValues(tooltips, valid_ids); - this.highlights = inputView.values[0].highlights ? extractValues(inputView.values[0].highlights, valid_ids) : inputView.values[0].highlights; - this.anyHighlights = this.highlights ? true : false - this.categories = inputView.categories[0]; - this.percentLabels = percent_labels; - this.scatter_formatting = extractValues(scatter_cond, valid_ids); } } diff --git a/src/Classes/plotPropertiesClass.ts b/src/Classes/plotPropertiesClass.ts index 0409205..2b7bacb 100644 --- a/src/Classes/plotPropertiesClass.ts +++ b/src/Classes/plotPropertiesClass.ts @@ -6,6 +6,7 @@ import { plotData } from "./viewModelClass" import settingsClass from "./settingsClass"; import dataClass from "./dataClass"; import controlLimitsClass from "./controlLimitsClass"; +import isNotNullOrUndefined from "../Functions/isNotNullOrUndefined"; export type axisProperties = { lower: number, @@ -52,7 +53,8 @@ export default class plotPropertiesClass { plotPoints: plotData[], controlLimits: controlLimitsClass, inputData: dataClass, - inputSettings: settingsClass }): void { + inputSettings: settingsClass, + invalidDataView: boolean }): void { // Get the width and height of plotting space this.width = args.options.viewport.width; @@ -60,7 +62,7 @@ export default class plotPropertiesClass { this.displayPlot = args.plotPoints ? args.plotPoints.length > 1 - : null; + : false; const xLowerLimit: number = args.inputSettings.x_axis.xlimit_l; let xUpperLimit: number = args.inputSettings.x_axis.xlimit_u; @@ -68,8 +70,10 @@ export default class plotPropertiesClass { let yUpperLimit: number = args.inputSettings.y_axis.ylimit_u; // Only update data-/settings-dependent plot aesthetics if they have changed - if (args.inputData && args.controlLimits) { - xUpperLimit = xUpperLimit !== null ? xUpperLimit : d3.max(args.controlLimits.keys.map(d => d.x)) + if (!args.invalidDataView) { + xUpperLimit = isNotNullOrUndefined(xUpperLimit) + ? xUpperLimit + : d3.max(args.controlLimits.keys.map(d => d.x)); const limitMultiplier: number = args.inputSettings.y_axis.limit_multiplier; const chart_type: string = args.inputSettings.spc.chart_type; @@ -77,10 +81,10 @@ export default class plotPropertiesClass { const ul99: number[] = args.controlLimits.ul99; const ll99: number[] = args.controlLimits.ll99; const alt_targets: number[] = args.controlLimits.alt_targets; - const maxValueOrLimit: number = d3.max(values.concat(ul99).concat(alt_targets)); - const minValueOrLimit: number = d3.min(values.concat(ll99).concat(alt_targets)); - const maxTarget: number = d3.max(args.controlLimits.targets); - const minTarget: number = d3.min(args.controlLimits.targets); + const maxValueOrLimit: number = d3.max((values.concat(ul99).concat(alt_targets))); + const minValueOrLimit: number = d3.min((values.concat(ll99).concat(alt_targets))); + const maxTarget: number = d3.max((args.controlLimits.targets)); + const minTarget: number = d3.min((args.controlLimits.targets)); const upperLimitRaw: number = maxTarget + (maxValueOrLimit - maxTarget) * limitMultiplier; const lowerLimitRaw: number = minTarget - (minTarget - minValueOrLimit) * limitMultiplier; diff --git a/src/Classes/settingsClass.ts b/src/Classes/settingsClass.ts index 625ea6a..31ffa42 100644 --- a/src/Classes/settingsClass.ts +++ b/src/Classes/settingsClass.ts @@ -1,6 +1,5 @@ import powerbi from "powerbi-visuals-api"; import DataView = powerbi.DataView; -import DataViewCategorical = powerbi.DataViewCategorical; import DataViewPropertyValue = powerbi.DataViewPropertyValue import VisualObjectInstanceEnumerationObject = powerbi.VisualObjectInstanceEnumerationObject; import VisualEnumerationInstanceKinds = powerbi.VisualEnumerationInstanceKinds; @@ -10,6 +9,7 @@ import { dataViewWildcard } from "powerbi-visuals-utils-dataviewutils"; import extractConditionalFormatting from "../Functions/extractConditionalFormatting"; import defaultSettings from "../defaultSettings" import { defaultSettingsType, defaultSettingsKey, settingsPaneGroupings } from "../defaultSettings"; +import isNotNullOrUndefined from "../Functions/isNotNullOrUndefined"; /** * This is the core class which controls the initialisation and @@ -40,13 +40,19 @@ export default class settingsClass implements defaultSettingsType { const allSettingGroups: string[] = Object.getOwnPropertyNames(this); allSettingGroups.forEach(settingGroup => { - const categoricalView: DataViewCategorical = inputView.categorical ? inputView.categorical : null; - const condFormatting: defaultSettingsType[defaultSettingsKey] = extractConditionalFormatting(categoricalView, settingGroup, this)[0]; + let condFormatting: defaultSettingsType[defaultSettingsKey] | undefined; + if (isNotNullOrUndefined(inputView.categorical)) { + if (isNotNullOrUndefined(inputView.categorical.categories)) { + condFormatting = extractConditionalFormatting(inputView.categorical, settingGroup, this)[0]; + } + } // Get the names of all settings in a given class and // use those to extract and update the relevant values const settingNames: string[] = Object.getOwnPropertyNames(this[settingGroup]); settingNames.forEach(settingName => { - this[settingGroup][settingName] = condFormatting ? condFormatting[settingName] : defaultSettings[settingGroup][settingName] + this[settingGroup][settingName] = isNotNullOrUndefined(condFormatting) + ? (condFormatting as defaultSettingsType[defaultSettingsKey])[settingName] + : defaultSettings[settingGroup][settingName] }) }) } diff --git a/src/Classes/viewModelClass.ts b/src/Classes/viewModelClass.ts index 4ea024f..ef65dbb 100644 --- a/src/Classes/viewModelClass.ts +++ b/src/Classes/viewModelClass.ts @@ -18,7 +18,7 @@ import * as limitFunctions from "../Limit Calculations" export class lineData { x: number; - line_value: number; + line_value: number | null; group: string; } @@ -46,23 +46,31 @@ export default class viewModelClass { firstRun: boolean; limitFunction: (inputData: dataClass, inputSettings: settingsClass) => controlLimitsClass; + constructor() { + this.inputData = new dataClass(); + this.inputSettings = new settingsClass(); + this.plotPoints = new Array(); + this.groupedLines = new Array<[string, lineData[]]>(); + this.plotProperties = new plotPropertiesClass(); + this.plotProperties.firstRun = true; + this.firstRun = true + this.splitIndexes = new Array(); + } + update(options: VisualUpdateOptions, host: IVisualHost) { - if (this.firstRun) { - this.inputSettings = new settingsClass(); - } const dv: powerbi.DataView[] = options.dataViews; this.inputSettings.update(dv[0]); - const split_indexes_storage: DataViewObject = dv[0].metadata.objects ? dv[0].metadata.objects.split_indexes_storage : null; - const split_indexes: DataViewPropertyValue = split_indexes_storage ? split_indexes_storage.split_indexes : null; + const split_indexes_storage: DataViewObject | null = dv[0].metadata.objects ? dv[0].metadata.objects.split_indexes_storage : null; + const split_indexes: DataViewPropertyValue | null = split_indexes_storage ? split_indexes_storage.split_indexes : null; this.splitIndexes = split_indexes ? JSON.parse((split_indexes)) : new Array(); + const invalidDataView: boolean = checkInvalidDataView(dv); // Make sure that the construction returns early with null members so // that the visual does not crash when trying to process invalid data - if (checkInvalidDataView(dv)) { - this.inputData = null; - this.limitFunction = null; - this.controlLimits = null; + if (invalidDataView) { + this.inputData = new dataClass(); + this.controlLimits = new controlLimitsClass(); this.plotPoints = new Array(); this.groupedLines = new Array<[string, lineData[]]>(); this.splitIndexes = new Array(); @@ -85,16 +93,13 @@ export default class viewModelClass { this.initialiseGroupedLines(); } } - if (this.firstRun) { - this.plotProperties = new plotPropertiesClass(); - this.plotProperties.firstRun = true; - } this.plotProperties.update({ options: options, plotPoints: this.plotPoints, controlLimits: this.controlLimits, inputData: this.inputData, - inputSettings: this.inputSettings + inputSettings: this.inputSettings, + invalidDataView: invalidDataView }) this.firstRun = false; } @@ -167,7 +172,7 @@ export default class viewModelClass { .withCategory(this.inputData.categories, this.inputData.keys[i].id) .createSelectionId(), - highlighted: this.inputData.highlights ? (this.inputData.highlights[index] ? true : false) : false, + highlighted: this.inputData.anyHighlights ? (this.inputData.highlights[index] ? true : false) : false, tooltip: buildTooltip(i, this.controlLimits, this.inputData, this.inputSettings) }) this.tickLabels.push({x: index, label: this.controlLimits.keys[i].label}); @@ -200,16 +205,4 @@ export default class viewModelClass { } this.groupedLines = d3.groups(formattedLines, d => d.group); } - - constructor() { - this.inputData = null; - this.inputSettings = null; - this.limitFunction = null; - this.controlLimits = null; - this.plotPoints = new Array(); - this.groupedLines = new Array<[string, lineData[]]>(); - this.plotProperties = null; - this.firstRun = true - this.splitIndexes = new Array(); - } } diff --git a/src/D3 Plotting Functions/Assurance Icons/consistentFail.ts b/src/D3 Plotting Functions/Assurance Icons/consistentFail.ts index fde0013..1d327ff 100644 --- a/src/D3 Plotting Functions/Assurance Icons/consistentFail.ts +++ b/src/D3 Plotting Functions/Assurance Icons/consistentFail.ts @@ -1,4 +1,4 @@ -import { svgBaseType } from "../../visual" +import { iconSelection } from "../drawIcons" /** * Inline function to be called by D3 for rendering the Assurance - Fail icon. @@ -9,7 +9,7 @@ import { svgBaseType } from "../../visual" * * @param selection The D3 parent object to which the icon's SVG code will be added */ -export default function consistentFail(selection: svgBaseType): void { +export default function consistentFail(selection: iconSelection): void { selection.append("g") .attr("clip-path","url(#clip2)") .append("g") diff --git a/src/D3 Plotting Functions/Assurance Icons/consistentPass.ts b/src/D3 Plotting Functions/Assurance Icons/consistentPass.ts index ae6e6b3..22ac4b5 100644 --- a/src/D3 Plotting Functions/Assurance Icons/consistentPass.ts +++ b/src/D3 Plotting Functions/Assurance Icons/consistentPass.ts @@ -1,4 +1,4 @@ -import { svgBaseType } from "../../visual" +import { iconSelection } from "../drawIcons" /** * Inline function to be called by D3 for rendering the Assurance - Pass icon. @@ -9,7 +9,7 @@ import { svgBaseType } from "../../visual" * * @param selection The D3 parent object to which the icon's SVG code will be added */ -export default function consistentPass(selection: svgBaseType): void { +export default function consistentPass(selection: iconSelection): void { selection.append("g") .attr("clip-path","url(#clip2)") .append("g") diff --git a/src/D3 Plotting Functions/Assurance Icons/inconsistent.ts b/src/D3 Plotting Functions/Assurance Icons/inconsistent.ts index 3b888b9..c7eb556 100644 --- a/src/D3 Plotting Functions/Assurance Icons/inconsistent.ts +++ b/src/D3 Plotting Functions/Assurance Icons/inconsistent.ts @@ -1,5 +1,4 @@ -import { svgBaseType } from "../../visual" - +import { iconSelection } from "../drawIcons" /** * Inline function to be called by D3 for rendering the Assurance - Iconsistent icon. * The code below is a translation from HTML to D3 syntax of the SVG file: @@ -9,7 +8,7 @@ import { svgBaseType } from "../../visual" * * @param selection The D3 parent object to which the icon's SVG code will be added */ -export default function inconsistent(selection: svgBaseType): void { +export default function inconsistent(selection: iconSelection): void { selection.append("g") .attr("clip-path","url(#clip2)") .append("g") diff --git a/src/D3 Plotting Functions/D3 Modules/index.ts b/src/D3 Plotting Functions/D3 Modules/index.ts index 047f8bf..1273deb 100644 --- a/src/D3 Plotting Functions/D3 Modules/index.ts +++ b/src/D3 Plotting Functions/D3 Modules/index.ts @@ -1,5 +1,17 @@ +import { min as d3min, max as d3max, mean as d3mean, median as d3median } from "d3-array"; + +/** + * D3 Array reduction functions have return type number | undefined, + * redefine these to remove the 'undefined' as that is handled separately + */ +const min = (x: number[]): number => d3min(x) as number; +const max = (x: number[]): number => d3max(x) as number; +const mean = (x: number[]): number => d3mean(x) as number; +const median = (x: number[]): number => d3median(x) as number; + export { select, selectAll, Selection, BaseType } from "d3-selection"; -export { groups, leastIndex, mean, median, sum, max, min } from "d3-array"; +export { groups, leastIndex, sum } from "d3-array"; +export { min, max, mean, median }; export { line } from "d3-shape" export { Axis, axisBottom, axisLeft } from "d3-axis" export { ScaleLinear, scaleLinear, NumberValue } from "d3-scale" diff --git a/src/D3 Plotting Functions/Variation Icons/commonCause.ts b/src/D3 Plotting Functions/Variation Icons/commonCause.ts index bf6b054..2441b82 100644 --- a/src/D3 Plotting Functions/Variation Icons/commonCause.ts +++ b/src/D3 Plotting Functions/Variation Icons/commonCause.ts @@ -1,4 +1,4 @@ -import { svgBaseType } from "../../visual"; +import { iconSelection } from "../drawIcons" /** * Inline function to be called by D3 for rendering the Variation - Common Cause icon. @@ -9,7 +9,7 @@ import { svgBaseType } from "../../visual"; * * @param selection The D3 parent object to which the icon's SVG code will be added */ -export default function commonCause(selection: svgBaseType): void { +export default function commonCause(selection: iconSelection): void { selection.append("g") .attr("clip-path","url(#clip2)") .append("g") diff --git a/src/D3 Plotting Functions/Variation Icons/concernHigh.ts b/src/D3 Plotting Functions/Variation Icons/concernHigh.ts index 6c38a28..6d69593 100644 --- a/src/D3 Plotting Functions/Variation Icons/concernHigh.ts +++ b/src/D3 Plotting Functions/Variation Icons/concernHigh.ts @@ -1,4 +1,4 @@ -import { svgBaseType } from "../../visual"; +import { iconSelection } from "../drawIcons" /** * Inline function to be called by D3 for rendering the Variation - Concern High icon. @@ -12,7 +12,7 @@ import { svgBaseType } from "../../visual"; // ESLint errors due to number of lines in function, but would reduce readability to separate further /* eslint-disable */ -export default function concernHigh(selection: svgBaseType): void { +export default function concernHigh(selection: iconSelection): void { selection.append("g") .attr("clip-path","url(#clip2)") .append("g") diff --git a/src/D3 Plotting Functions/Variation Icons/concernLow.ts b/src/D3 Plotting Functions/Variation Icons/concernLow.ts index bdece3c..f410c09 100644 --- a/src/D3 Plotting Functions/Variation Icons/concernLow.ts +++ b/src/D3 Plotting Functions/Variation Icons/concernLow.ts @@ -1,4 +1,4 @@ -import { svgBaseType } from "../../visual" +import { iconSelection } from "../drawIcons" /** * Inline function to be called by D3 for rendering the Variation - Concern Low icon. @@ -9,7 +9,7 @@ import { svgBaseType } from "../../visual" * * @param selection The D3 parent object to which the icon's SVG code will be added */ -export default function concernLow(selection: svgBaseType): void { +export default function concernLow(selection: iconSelection): void { selection.append("g") .attr("clip-path","url(#clip2)") .append("g") diff --git a/src/D3 Plotting Functions/Variation Icons/improvementHigh.ts b/src/D3 Plotting Functions/Variation Icons/improvementHigh.ts index 2daa0c7..88945ca 100644 --- a/src/D3 Plotting Functions/Variation Icons/improvementHigh.ts +++ b/src/D3 Plotting Functions/Variation Icons/improvementHigh.ts @@ -1,4 +1,4 @@ -import { svgBaseType } from "../../visual" +import { iconSelection } from "../drawIcons" /** * Inline function to be called by D3 for rendering the Variation - Improvement High icon. @@ -12,7 +12,7 @@ import { svgBaseType } from "../../visual" // ESLint errors due to number of lines in function, but would reduce readability to separate further /* eslint-disable */ -export default function improvementHigh(selection: svgBaseType): void { +export default function improvementHigh(selection: iconSelection): void { selection.append("g") .attr("clip-path","url(#clip2)") .append("g") diff --git a/src/D3 Plotting Functions/Variation Icons/improvementLow.ts b/src/D3 Plotting Functions/Variation Icons/improvementLow.ts index 87e288e..6888df9 100644 --- a/src/D3 Plotting Functions/Variation Icons/improvementLow.ts +++ b/src/D3 Plotting Functions/Variation Icons/improvementLow.ts @@ -1,4 +1,4 @@ -import { svgBaseType } from "../../visual" +import { iconSelection } from "../drawIcons" /** * Inline function to be called by D3 for rendering the Variation - Improvement Low icon. @@ -9,7 +9,7 @@ import { svgBaseType } from "../../visual" * * @param selection The D3 parent object to which the icon's SVG code will be added */ -export default function improvementLow(selection: svgBaseType): void { +export default function improvementLow(selection: iconSelection): void { selection.append("g") .attr("clip-path","url(#clip2)") .append("g") diff --git a/src/D3 Plotting Functions/Variation Icons/neutralHigh.ts b/src/D3 Plotting Functions/Variation Icons/neutralHigh.ts index 1ea151e..3ffe560 100644 --- a/src/D3 Plotting Functions/Variation Icons/neutralHigh.ts +++ b/src/D3 Plotting Functions/Variation Icons/neutralHigh.ts @@ -1,4 +1,4 @@ -import { svgBaseType } from "../../visual" +import { iconSelection } from "../drawIcons" /** * Inline function to be called by D3 for rendering the Variation - Neutral High icon. @@ -9,7 +9,7 @@ import { svgBaseType } from "../../visual" * * @param selection The D3 parent object to which the icon's SVG code will be added */ -export default function neutralHigh(selection: svgBaseType): void { +export default function neutralHigh(selection: iconSelection): void { selection.append("g") .attr("clip-path","url(#clip2)") .append("g") diff --git a/src/D3 Plotting Functions/Variation Icons/neutralLow.ts b/src/D3 Plotting Functions/Variation Icons/neutralLow.ts index dba1f5c..ab6ed06 100644 --- a/src/D3 Plotting Functions/Variation Icons/neutralLow.ts +++ b/src/D3 Plotting Functions/Variation Icons/neutralLow.ts @@ -1,4 +1,4 @@ -import { svgBaseType } from "../../visual" +import { iconSelection } from "../drawIcons" /** * Inline function to be called by D3 for rendering the Variation - Neutral Low icon. @@ -9,7 +9,7 @@ import { svgBaseType } from "../../visual" * * @param selection The D3 parent object to which the icon's SVG code will be added */ -export default function neutralLow(selection: svgBaseType): void { +export default function neutralLow(selection: iconSelection): void { selection.append("g") .attr("clip-path","url(#clip2)") .append("g") diff --git a/src/D3 Plotting Functions/drawDots.ts b/src/D3 Plotting Functions/drawDots.ts index 049b7b5..15fd3c3 100644 --- a/src/D3 Plotting Functions/drawDots.ts +++ b/src/D3 Plotting Functions/drawDots.ts @@ -33,7 +33,7 @@ export default function drawDots(selection: svgBaseType, visualObj: Visual) { visualObj.host.persistProperties({ replace: [{ objectName: "split_indexes_storage", - selector: undefined, + selector: {}, properties: { split_indexes: JSON.stringify(visualObj.viewModel.splitIndexes) } }] }); diff --git a/src/D3 Plotting Functions/drawIcons.ts b/src/D3 Plotting Functions/drawIcons.ts index 62aba68..6d77a9c 100644 --- a/src/D3 Plotting Functions/drawIcons.ts +++ b/src/D3 Plotting Functions/drawIcons.ts @@ -1,10 +1,13 @@ import * as variationIcon from "./Variation Icons" import * as assuranceIcon from "./Assurance Icons" +import * as d3 from "./D3 Modules" import variationIconsToDraw from "../Functions/variationIconsToDraw"; import initialiseIconSVG from "./initialiseIconSVG"; import { svgBaseType, Visual } from "../visual"; import assuranceIconToDraw from "../Functions/assuranceIconToDraw"; +export type iconSelection = d3.Selection; + export default function drawIcons(selection: svgBaseType, visualObj: Visual): void { selection.selectAll(".icongroup").remove() if (!(visualObj.viewModel.plotProperties.displayPlot)) { @@ -33,6 +36,7 @@ export default function drawIcons(selection: svgBaseType, visualObj: Visual): vo const assurance_location: string = visualObj.viewModel.inputSettings.nhs_icons.assurance_icons_locations; const assurance_scaling: number = visualObj.viewModel.inputSettings.nhs_icons.assurance_icons_scaling; const assuranceIconPresent: string = assuranceIconToDraw(visualObj.viewModel); + if (assuranceIconPresent === "none") { return; } diff --git a/src/D3 Plotting Functions/drawLines.ts b/src/D3 Plotting Functions/drawLines.ts index a833405..3d1a6cf 100644 --- a/src/D3 Plotting Functions/drawLines.ts +++ b/src/D3 Plotting Functions/drawLines.ts @@ -15,7 +15,7 @@ export default function drawLines(selection: svgBaseType, visualObj: Visual) { const upper: number = visualObj.viewModel.plotProperties.yAxis.upper; return d3.line() .x(d => visualObj.viewModel.plotProperties.xScale(d.x)) - .y(d => visualObj.viewModel.plotProperties.yScale(d.line_value)) + .y(d => visualObj.viewModel.plotProperties.yScale(d.line_value as number)) .defined(d => d.line_value !== null && between(d.line_value, lower, upper))(d[1]) }) .attr("fill", "none") diff --git a/src/D3 Plotting Functions/drawTooltipLine.ts b/src/D3 Plotting Functions/drawTooltipLine.ts index e19cc8f..e0d4dc5 100644 --- a/src/D3 Plotting Functions/drawTooltipLine.ts +++ b/src/D3 Plotting Functions/drawTooltipLine.ts @@ -32,7 +32,7 @@ export default function drawTooltipLine(selection: svgBaseType, visualObj: Visua const xValue: number = plotProperties.xScale.invert(event.pageX); const xRange: number[] = plotPoints.map(d => d.x).map(d => Math.abs(d - xValue)); - const nearestDenominator: number = d3.leastIndex(xRange,(a,b) => a-b); + const nearestDenominator: number = d3.leastIndex(xRange,(a,b) => a-b) as number; const x_coord: number = plotProperties.xScale(plotPoints[nearestDenominator].x) const y_coord: number = plotProperties.yScale(plotPoints[nearestDenominator].value) diff --git a/src/D3 Plotting Functions/drawYAxis.ts b/src/D3 Plotting Functions/drawYAxis.ts index 56a8ce1..ffb14bf 100644 --- a/src/D3 Plotting Functions/drawYAxis.ts +++ b/src/D3 Plotting Functions/drawYAxis.ts @@ -18,10 +18,10 @@ export default function drawYAxis(selection: svgBaseType, visualObj: Visual, ref } if (visualObj.viewModel.inputData) { yAxis.tickFormat( - (d: number) => { + (d: d3.NumberValue) => { return visualObj.viewModel.inputData.percentLabels - ? (d * (multiplier === 100 ? 1 : (multiplier === 1 ? 100 : multiplier))).toFixed(sig_figs) + "%" - : d.toFixed(sig_figs); + ? (d * (multiplier === 100 ? 1 : (multiplier === 1 ? 100 : multiplier))).toFixed(sig_figs) + "%" + : (d).toFixed(sig_figs); } ); } diff --git a/src/D3 Plotting Functions/updateHighlighting.ts b/src/D3 Plotting Functions/updateHighlighting.ts index 5a9bf47..19d4efc 100644 --- a/src/D3 Plotting Functions/updateHighlighting.ts +++ b/src/D3 Plotting Functions/updateHighlighting.ts @@ -1,8 +1,11 @@ import powerbi from "powerbi-visuals-api"; +import * as d3 from "./D3 Modules" import { plotData } from "../Classes/viewModelClass"; import ISelectionId = powerbi.visuals.ISelectionId; import { svgBaseType, Visual } from "../visual"; +type dotSelection = d3.Selection + export default function updateHighlighting(selection: svgBaseType, visualObj: Visual) { const anyHighlights: boolean = visualObj.viewModel.inputData ? visualObj.viewModel.inputData.anyHighlights : false; const allSelectionIDs: ISelectionId[] = visualObj.selectionManager.getSelectionIds() as ISelectionId[]; @@ -16,7 +19,8 @@ export default function updateHighlighting(selection: svgBaseType, visualObj: Vi selection.selectAll(".dotsgroup").selectChildren().style("fill-opacity", defaultOpacity); selection.selectAll(".linesgroup").style("stroke-opacity", defaultOpacity); if (anyHighlights || (allSelectionIDs.length > 0)) { - selection.selectAll(".dotsgroup").selectChildren().style("fill-opacity", (dot: plotData) => { + const dotSelection: dotSelection = selection.selectAll(".dotsgroup").selectChildren(); + dotSelection.style("fill-opacity", (dot: any) => { const currentPointSelected: boolean = allSelectionIDs.some((currentSelectionId: ISelectionId) => { return currentSelectionId.includes(dot.identity); }); diff --git a/src/Functions/BinaryFunctions.ts b/src/Functions/BinaryFunctions.ts index 01a597b..3e0fa76 100644 --- a/src/Functions/BinaryFunctions.ts +++ b/src/Functions/BinaryFunctions.ts @@ -1,4 +1,4 @@ -type ReturnT = Input1T extends Array ? BaseT[] : Input2T extends Array ? BaseT[] : BaseT; +type ReturnT = Input1T extends Array ? NonNullable[] : Input2T extends Array ? NonNullable[] : NonNullable; export default function broadcast_binary(fun: (x: ScalarInput1T, y: ScalarInput2T) => ScalarReturnT) { return function x + y); export const subtract = broadcast_binary((x: number, y: number): number => x - y); export const divide = broadcast_binary((x: number, y: number): number => x / y); export const multiply = broadcast_binary( - (x: number, y: number): number => { + (x: number, y: number): number | null => { return (x === null || y === null) ? null : (x * y); }); diff --git a/src/Functions/Constants.ts b/src/Functions/Constants.ts index cd2653c..8bee7e2 100644 --- a/src/Functions/Constants.ts +++ b/src/Functions/Constants.ts @@ -2,28 +2,23 @@ import { sqrt, exp, lgamma, square } from "./UnaryFunctions"; import broadcast_unary from "./UnaryFunctions"; import broadcast_binary from "./BinaryFunctions"; -export const c4 = broadcast_unary( - (sampleSize: number): number => { - if ((sampleSize <= 1) || (sampleSize === null)) { - return null; - } - const Nminus1: number = sampleSize - 1; +function c4(sampleSize: number): number { + const Nminus1: number = sampleSize - 1; - return sqrt(2.0 / Nminus1) - * exp(lgamma(sampleSize / 2.0) - lgamma(Nminus1 / 2.0)); - } -); + return sqrt(2.0 / Nminus1) + * exp(lgamma(sampleSize / 2.0) - lgamma(Nminus1 / 2.0)); +} -export const c5 = broadcast_unary( - (sampleSize: number): number => { - return sqrt(1 - square(c4(sampleSize))); - } -); +function c5(sampleSize: number): number { + return sqrt(1 - square(c4(sampleSize) as number)); +} export const a3 = broadcast_unary( - (sampleSize: number): number => { - const filt_samp: number = sampleSize <= 1 ? null : sampleSize; - return 3.0 / (c4(filt_samp) * sqrt(filt_samp)); + (sampleSize: number): number | null => { + if ((sampleSize === null) || (sampleSize <= 1)) { + return null; + } + return 3.0 / (c4(sampleSize) as number * sqrt(sampleSize)); } ); @@ -35,13 +30,19 @@ const b_helper = broadcast_binary( ) export const b3 = broadcast_binary( - (sampleSize: number, use95: boolean): number => { + (sampleSize: number, use95: boolean): number | null => { + if ((sampleSize === null) || (sampleSize <= 1)) { + return null; + } return 1 - b_helper(sampleSize, use95); } ); export const b4 = broadcast_binary( - (sampleSize: number, use95: boolean): number => { + (sampleSize: number, use95: boolean): number | null => { + if ((sampleSize === null) || (sampleSize <= 1)) { + return null; + } return 1 + b_helper(sampleSize, use95); } ); diff --git a/src/Functions/UnaryFunctions.ts b/src/Functions/UnaryFunctions.ts index 34552d1..d6d2cd2 100644 --- a/src/Functions/UnaryFunctions.ts +++ b/src/Functions/UnaryFunctions.ts @@ -1,6 +1,6 @@ import gammaln from "@stdlib/math-base-special-gammaln"; -type ReturnT = InputT extends Array ? BaseT[] : BaseT; +type ReturnT = InputT extends Array ? NonNullable[] : NonNullable; export default function broadcast_unary(fun: (x: ScalarInputT) => ScalarReturnT) { return function(y: T): ReturnT { diff --git a/src/Functions/buildTooltip.ts b/src/Functions/buildTooltip.ts index 84136d9..fb23420 100644 --- a/src/Functions/buildTooltip.ts +++ b/src/Functions/buildTooltip.ts @@ -26,8 +26,8 @@ export default function buildTooltip(index: number, controlLimits: controlLimits const date: string = controlLimits.keys[index].label; const value: number = controlLimits.values[index]; - const numerator: number = controlLimits.numerators ? controlLimits.numerators[index] : null; - const denominator: number = controlLimits.denominators ? controlLimits.denominators[index] : null; + const numerator: number | null = controlLimits.numerators ? controlLimits.numerators[index] : null; + const denominator: number | null = controlLimits.denominators ? controlLimits.denominators[index] : null; const target: number = controlLimits.targets[index]; const limits = { ll99: controlLimits.ll99 ? controlLimits.ll99[index] : null, @@ -71,7 +71,7 @@ export default function buildTooltip(index: number, controlLimits: controlLimits if (chart_type !== "run") { tooltip.push({ displayName: "Upper 99% Limit", - value: (limits.ul99 * multiplier).toFixed(sig_figs) + suffix + value: (limits.ul99 !== null) ? (limits.ul99 * multiplier).toFixed(sig_figs) + suffix : "" }) } tooltip.push({ @@ -81,7 +81,7 @@ export default function buildTooltip(index: number, controlLimits: controlLimits if (chart_type !== "run") { tooltip.push({ displayName: "Lower 99% Limit", - value: (limits.ll99 * multiplier).toFixed(sig_figs) + suffix + value: (limits.ll99 !== null) ? (limits.ll99 * multiplier).toFixed(sig_figs) + suffix : "" }) } diff --git a/src/Functions/checkInvalidDataView.ts b/src/Functions/checkInvalidDataView.ts index 6469d21..4f0bc82 100644 --- a/src/Functions/checkInvalidDataView.ts +++ b/src/Functions/checkInvalidDataView.ts @@ -12,21 +12,17 @@ export default function checkInvalidDataView(inputDV: powerbi.DataView[]): boole return flag1; } - const flag2: boolean = - !inputDV[0].categorical.categories[0].source + const inputView: powerbi.DataViewCategorical = inputDV[0].categorical as powerbi.DataViewCategorical; + const inputCategories: powerbi.DataViewCategoryColumn[] = inputView.categories as powerbi.DataViewCategoryColumn[]; + const inputValues: powerbi.DataViewValueColumns = inputView.values as powerbi.DataViewValueColumns; - if (flag2) { - return flag2; + if (!inputCategories[0].source) { + return true; } - const flag3: boolean = - !inputDV[0].categorical.values[0].source.roles.numerators - if (flag3) { - return flag3; + if (!(inputValues[0].source.roles as Record).numerators) { + return true; } - const flag4: boolean = - inputDV[0].categorical.values.some(d => d.values.length < 1) - || inputDV[0].categorical.categories.some(d => d.values.length < 1); - return flag4; + return inputValues.some(d => d.values.length < 1) || inputCategories.some(d => d.values.length < 1); } diff --git a/src/Functions/checkValidInput.ts b/src/Functions/checkValidInput.ts index 6499f65..3141333 100644 --- a/src/Functions/checkValidInput.ts +++ b/src/Functions/checkValidInput.ts @@ -1,4 +1,4 @@ -export default function checkValidInput(numerator: number, denominator: number, xbar_sd: number, data_type: string): boolean { +export default function checkValidInput(numerator: number, denominator: number | null, xbar_sd: number | null, data_type: string): boolean { const denominatorConstraintRequired: string[] = ["p", "pprime", "u", "uprime"]; const denominatorRequired: string[] = ["p", "pprime", "u", "uprime", "xbar", "s"]; const denominatorConstraintForRunIChart: string[] = ["i", "run"]; @@ -7,7 +7,7 @@ export default function checkValidInput(numerator: number, denominator: number, ? denominator !== null && denominator !== undefined && denominator > 0 : true; const proportionDenominatorValid: boolean = denominatorConstraintRequired.includes(data_type) - ? (numerator <= denominator) + ? (numerator <= (denominator as number)) : true; const runIChartDenominatorValid: boolean = (denominatorConstraintForRunIChart.includes(data_type) && !(denominator === null)) diff --git a/src/Functions/dateToFormattedString.ts b/src/Functions/dateToFormattedString.ts index b58d626..43ce182 100644 --- a/src/Functions/dateToFormattedString.ts +++ b/src/Functions/dateToFormattedString.ts @@ -49,7 +49,7 @@ const dateToFormattedString = broadcast_binary( const formatString: string = `{ ${localeDateMap[inpLocale]}, "options": { "day" : "2-digit", ${monthDateMap[inpMonth]}, ${yearDateMap[inpYear]} }, ${delimDateMap[inpDelim]} }`; const date_format: dateFormat = JSON.parse(formatString); const formattedString: string = input_date.toLocaleDateString(date_format.locale, date_format.options) - .replace(/(\/|(\s|,\s))/gi, date_format.delimiter); + .replace(/(\/|(\s|,\s))/gi, date_format.delimiter as string); if (inpDay !== "DD") { const weekday: string = input_date.toLocaleDateString(date_format.locale, {weekday : weekdayDateMap[inpDay]}) return weekday + " " + formattedString; diff --git a/src/Functions/diff.ts b/src/Functions/diff.ts index 73e6a07..cb5b0b1 100644 --- a/src/Functions/diff.ts +++ b/src/Functions/diff.ts @@ -1,7 +1,3 @@ export default function diff(x: number[]): number[] { - const consec_diff: number[] = new Array(x.length - 1); - for (let i = 1; i < x.length; i++) { - consec_diff[(i-1)] = (x[i] - x[(i-1)]); - } - return [null].concat(consec_diff); + return x.map((d, idx, arr) => idx > 0 ? d - arr[idx - 1] : null) as number[] } diff --git a/src/Functions/extractConditionalFormatting.ts b/src/Functions/extractConditionalFormatting.ts index 3fbf3d7..daa37e8 100644 --- a/src/Functions/extractConditionalFormatting.ts +++ b/src/Functions/extractConditionalFormatting.ts @@ -8,12 +8,6 @@ import defaultSettings, { defaultSettingsType, defaultSettingsKey } from "../def type SettingsTypes = defaultSettingsType[defaultSettingsKey]; export default function extractConditionalFormatting(categoricalView: DataViewCategorical, name: string, inputSettings: settingsClass): SettingsTypes[] { - if (categoricalView === null) { - return [null]; - } - if ((categoricalView.categories === null) || (categoricalView.categories === undefined)) { - return [null]; - } const inputCategories: DataViewCategoryColumn = (categoricalView.categories as DataViewCategoryColumn[])[0]; const settingNames = Object.getOwnPropertyNames(inputSettings[name]); diff --git a/src/Functions/extractDataColumn.ts b/src/Functions/extractDataColumn.ts index c9193f4..a22c306 100644 --- a/src/Functions/extractDataColumn.ts +++ b/src/Functions/extractDataColumn.ts @@ -29,22 +29,22 @@ export default function extractDataColumn(inputView: DataView } else { columnRaw = columnRawTmp[0]; } - if (columnRaw.source.type.dateTime) { + if (columnRaw.source.type?.dateTime) { return dateToFormattedString(columnRaw.values, inputSettings.dates) as Extract; } else { return columnRaw.values as Extract; } } else if (name === "tooltips") { let rtn = new Array(); - const tooltipColumns = inputView.values.filter(viewColumn => viewColumn.source.roles.tooltips); + const tooltipColumns = inputView.values!.filter(viewColumn => viewColumn.source.roles?.tooltips); if (tooltipColumns.length > 0) { rtn = tooltipColumns[0].values.map((_, idx) => { return tooltipColumns.map(viewColumn => { return { displayName: viewColumn.source.displayName, - value: viewColumn.source.type.numeric + value: viewColumn.source.type?.numeric ? ((viewColumn.values[idx])).toString() - : viewColumn.source.type.dateTime + : viewColumn.source.type?.dateTime ? dateToFormattedString((viewColumn.values[idx]), inputSettings.dates) : (viewColumn.values[idx]) } diff --git a/src/Functions/isNotNullOrUndefined.ts b/src/Functions/isNotNullOrUndefined.ts new file mode 100644 index 0000000..9e18bee --- /dev/null +++ b/src/Functions/isNotNullOrUndefined.ts @@ -0,0 +1,3 @@ +export default function isNotNullOrUndefined(x: T | null | undefined): x is T { + return (x !== null) && (x !== undefined); +} diff --git a/src/Limit Calculations/mr.ts b/src/Limit Calculations/mr.ts index 1759bd9..8e990c9 100644 --- a/src/Limit Calculations/mr.ts +++ b/src/Limit Calculations/mr.ts @@ -20,8 +20,8 @@ export default function mrLimits(inputData: dataClass, inputSettings: settingsCl inputSettings: inputSettings, keys: inputData.keys, values: consec_diff, - numerators: useRatio ? inputData.numerators : null, - denominators: useRatio ? inputData.denominators : null, + numerators: useRatio ? inputData.numerators : undefined, + denominators: useRatio ? inputData.denominators : undefined, targets: rep(cl, inputData.keys.length), ll99: rep(0, inputData.keys.length), ll95: rep(0, inputData.keys.length), diff --git a/src/Limit Calculations/run.ts b/src/Limit Calculations/run.ts index 2479152..1731570 100644 --- a/src/Limit Calculations/run.ts +++ b/src/Limit Calculations/run.ts @@ -16,12 +16,12 @@ export default function runLimits(inputData: dataClass, inputSettings: settingsC inputSettings: inputSettings, keys: inputData.keys, values: ratio.map(d => isNaN(d) ? 0 : d), - numerators: useRatio ? inputData.numerators : null, - denominators: useRatio ? inputData.denominators : null, + numerators: useRatio ? inputData.numerators : undefined, + denominators: useRatio ? inputData.denominators : undefined, targets: rep(cl, inputData.keys.length), - ll99: null, - ll95: null, - ul95: null, - ul99: null + ll99: new Array(), + ll95: new Array(), + ul95: new Array(), + ul99: new Array() }); } diff --git a/src/Limit Calculations/t.ts b/src/Limit Calculations/t.ts index 4037f45..4b1af49 100644 --- a/src/Limit Calculations/t.ts +++ b/src/Limit Calculations/t.ts @@ -9,7 +9,7 @@ export default function tLimits(inputData: dataClass, inputSettings: settingsCla const val: number[] = pow(inputData.numerators, 1 / 3.6); const inputDataCopy: dataClass = JSON.parse(JSON.stringify(inputData)); inputDataCopy.numerators = val; - inputDataCopy.denominators = null; + inputDataCopy.denominators = new Array(); const limits: controlLimitsClass = iLimits(inputDataCopy, inputSettings); limits.targets = pow(limits.targets, 3.6); limits.values = pow(limits.values, 3.6); diff --git a/src/defaultSettings.ts b/src/defaultSettings.ts index b2823eb..1892747 100644 --- a/src/defaultSettings.ts +++ b/src/defaultSettings.ts @@ -12,9 +12,9 @@ const defaultSettings = { sig_figs: 2, perc_labels: "Automatic", split_on_click: false, - ll_truncate: (null), - ul_truncate: (null), - alt_target: (null) + ll_truncate: (null), + ul_truncate: (null), + alt_target: (null) }, outliers: { process_flag_type: "both", @@ -81,13 +81,13 @@ const defaultSettings = { xlimit_tick_size: 10, xlimit_tick_colour: "#000000", xlimit_tick_rotation: -35, - xlimit_tick_count: (null), - xlimit_label:(null), + xlimit_tick_count: (null), + xlimit_label:"", xlimit_label_font: "'Arial', sans-serif", xlimit_label_size: 10, xlimit_label_colour: "#000000", - xlimit_l: (null), - xlimit_u: (null) + xlimit_l: (null), + xlimit_u: (null) }, y_axis: { ylimit_colour: "#000000", @@ -96,15 +96,15 @@ const defaultSettings = { ylimit_tick_size: 10, ylimit_tick_colour: "#000000", ylimit_tick_rotation: -35, - ylimit_tick_count: (null), - ylimit_label:(null), + ylimit_tick_count: (null), + ylimit_label:"", ylimit_label_font: "'Arial', sans-serif", ylimit_label_size: 10, ylimit_label_colour: "#000000", - ylimit_l: (null), - ylimit_u: (null), + ylimit_l: (null), + ylimit_u: (null), limit_multiplier: 1.5, - ylimit_sig_figs: (null) + ylimit_sig_figs: (null) }, dates: { date_format_day: "DD", diff --git a/tsconfig.json b/tsconfig.json index b68a445..e5f062b 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -9,6 +9,7 @@ "noImplicitThis": true, "noUnusedLocals": true, "noUnusedParameters": true, + "noImplicitOverride": true, "strictBindCallApply": true, "emitDecoratorMetadata": true, "experimentalDecorators": true,