From abb8b84864b10a01db00b877bca37954ac89be9c Mon Sep 17 00:00:00 2001 From: Andrew Johnson Date: Mon, 18 Mar 2024 02:35:18 +0200 Subject: [PATCH] Improve typing for settings --- src/Classes/settingsClass.ts | 73 ++++++++++++++++++++++++------------ src/defaultSettings.ts | 17 ++++----- src/visual.ts | 4 +- 3 files changed, 58 insertions(+), 36 deletions(-) diff --git a/src/Classes/settingsClass.ts b/src/Classes/settingsClass.ts index 3489299..4a45922 100644 --- a/src/Classes/settingsClass.ts +++ b/src/Classes/settingsClass.ts @@ -1,6 +1,5 @@ import * as powerbi from "powerbi-visuals-api" type DataView = powerbi.default.DataView; -type DataViewCategorical = powerbi.default.DataViewCategorical; type DataViewPropertyValue = powerbi.default.DataViewPropertyValue type VisualObjectInstanceEnumerationObject = powerbi.default.VisualObjectInstanceEnumerationObject; type VisualObjectInstance = powerbi.default.VisualObjectInstance; @@ -10,10 +9,19 @@ import { extractConditionalFormatting } from "../Functions"; import { default as defaultSettings, type settingsValueTypes, settingsPaneGroupings, settingsPaneToggles } from "../defaultSettings"; import derivedSettingsClass from "./derivedSettingsClass"; +type NestedKeysOf + = T extends object + ? { [K in keyof T]: K extends string ? K : never; }[keyof T] + : never; + export type defaultSettingsType = settingsValueTypes; export type defaultSettingsKey = keyof defaultSettingsType; +export type defaultSettingsNestedKey = NestedKeysOf; export type settingsScalarTypes = number | string | boolean; +export type paneGroupingsNestedKey = "all" | NestedKeysOf; +export type paneTogglesNestedKey = "all" | NestedKeysOf; + /** * This is the core class which controls the initialisation and * updating of user-settings. Each member is its own class defining @@ -35,14 +43,16 @@ export default class settingsClass { // Get the names of all classes in settingsObject which have values to be updated const allSettingGroups: string[] = Object.keys(this.settings); - allSettingGroups.forEach(settingGroup => { - const categoricalView: DataViewCategorical = inputView.categorical ? inputView.categorical : null; - const condFormatting: defaultSettingsType[defaultSettingsKey] = extractConditionalFormatting(categoricalView, settingGroup, this.settings)[0]; + allSettingGroups.forEach((settingGroup: defaultSettingsKey) => { + const condFormatting: defaultSettingsType[defaultSettingsKey] + = extractConditionalFormatting(inputView?.categorical, settingGroup, this.settings)[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.keys(this.settings[settingGroup]); - settingNames.forEach(settingName => { - this.settings[settingGroup][settingName] = condFormatting ? condFormatting[settingName] : defaultSettings[settingGroup][settingName]["default"] + settingNames.forEach((settingName: defaultSettingsNestedKey) => { + this.settings[settingGroup][settingName] + = condFormatting ? condFormatting[settingName] : defaultSettings[settingGroup][settingName]["default"] }) }) @@ -50,41 +60,54 @@ export default class settingsClass { } /** - * Function to extract all values for a given settings group, which are then - * rendered to the Settings pane in PowerBI + * Get the names of all settings in a given group, and remove any which are toggled off. * * @param settingGroupName - * @param inputData - * @returns An object where each element is the value for a given setting in the named group + * @returns */ - createSettingsEntry(settingGroupName: string): VisualObjectInstanceEnumerationObject { - const settingNames: string[] = Object.keys(this.settings[settingGroupName]); - const settingsGrouped: boolean = Object.keys(settingsPaneGroupings).includes(settingGroupName); - const paneGroupings: Record - = settingsGrouped ? JSON.parse(JSON.stringify(settingsPaneGroupings[settingGroupName])) - : { "all": settingNames }; + getSettingNames(settingGroupName: defaultSettingsKey): Record { + const settingsGrouped: boolean = Object.keys(settingsPaneGroupings) + .includes(settingGroupName); + const paneGroupings: Record + = settingsGrouped + ? JSON.parse(JSON.stringify(settingsPaneGroupings[settingGroupName])) + : { "all": Object.keys(this.settings[settingGroupName]) }; if (Object.keys(settingsPaneToggles).includes(settingGroupName)) { - const toggledSettings: Record> - = settingsGrouped ? settingsPaneToggles[settingGroupName] - : { "all": settingsPaneToggles[settingGroupName]}; + const toggledSettings: Record + = settingsGrouped + ? settingsPaneToggles[settingGroupName] + : { "all": settingsPaneToggles[settingGroupName]}; - Object.keys(toggledSettings).forEach(toggleGroup => { - const possibleSettings: string[] = paneGroupings[toggleGroup]; + Object.keys(toggledSettings).forEach((toggleGroup: paneGroupingsNestedKey) => { let settingsToRemove: string[] = new Array(); - Object.keys(toggledSettings[toggleGroup]).forEach(settingToggle => { + Object.keys(toggledSettings[toggleGroup]).forEach((settingToggle: paneTogglesNestedKey) => { if (this.settings[settingGroupName][settingToggle] !== true) { settingsToRemove = settingsToRemove.concat(toggledSettings[toggleGroup][settingToggle]) } }) - paneGroupings[toggleGroup] = possibleSettings.filter(setting => !settingsToRemove.includes(setting)) + paneGroupings[toggleGroup] = paneGroupings[toggleGroup].filter(setting => !settingsToRemove.includes(setting)) }) } + return paneGroupings; + } + + /** + * Function to extract all values for a given settings group, which are then + * rendered to the Settings pane in PowerBI + * + * @param settingGroupName + * @param inputData + * @returns An object where each element is the value for a given setting in the named group + */ + createSettingsEntry(settingGroupName: defaultSettingsKey): VisualObjectInstanceEnumerationObject { + const paneGroupings: Record + = this.getSettingNames(settingGroupName); const rtnInstances = new Array(); const rtnContainers = new Array(); - Object.keys(paneGroupings).forEach((currKey, idx) => { + Object.keys(paneGroupings).forEach((currKey: paneGroupingsNestedKey, idx) => { const props = Object.fromEntries( paneGroupings[currKey].map(currSetting => { const settingValue: DataViewPropertyValue = this.settings[settingGroupName][currSetting] @@ -106,7 +129,7 @@ export default class settingsClass { properties: props, propertyInstanceKind: Object.fromEntries(propertyKinds), selector: dataViewWildcard.createDataViewWildcardSelector(dataViewWildcard.DataViewWildcardMatchingOption.InstancesAndTotals), - validValues: Object.fromEntries(Object.keys(defaultSettings[settingGroupName]).map((settingName) => { + validValues: Object.fromEntries(Object.keys(defaultSettings[settingGroupName]).map((settingName: defaultSettingsNestedKey) => { return [settingName, defaultSettings[settingGroupName][settingName]?.["valid"]] })) }) diff --git a/src/defaultSettings.ts b/src/defaultSettings.ts index 9ca41d9..e17b806 100644 --- a/src/defaultSettings.ts +++ b/src/defaultSettings.ts @@ -179,23 +179,22 @@ const defaultSettings = { } }; -type RemoveValidEntry = { - [K in keyof T as Exclude]: T[K] -}; -type Unwrap = T[keyof RemoveValidEntry]; + +type DefaultTypes = T[Extract]; export type settingsValueTypes = { [K in keyof typeof defaultSettings]: { - [L in keyof typeof defaultSettings[K]]: Unwrap + [L in keyof typeof defaultSettings[K]]: DefaultTypes } } export const settingsPaneGroupings = { outliers: { - "Astronomical Points": ["process_flag_type", "improvement_direction", "astronomical", "astronomical_limit", "ast_colour_improvement", "ast_colour_deterioration", "ast_colour_neutral_low", "ast_colour_neutral_high"], - "Shifts": ["process_flag_type", "improvement_direction", "shift", "shift_n", "shift_colour_improvement", "shift_colour_deterioration", "shift_colour_neutral_low", "shift_colour_neutral_high"], - "Trends": ["process_flag_type", "improvement_direction", "trend", "trend_n", "trend_colour_improvement", "trend_colour_deterioration", "trend_colour_neutral_low", "trend_colour_neutral_high"], - "Two-In-Three": ["process_flag_type", "improvement_direction", "two_in_three", "two_in_three_highlight_series", "two_in_three_limit", "twointhree_colour_improvement", "twointhree_colour_deterioration", "twointhree_colour_neutral_low", "twointhree_colour_neutral_high"] + "General": ["process_flag_type", "improvement_direction"], + "Astronomical Points": ["astronomical", "astronomical_limit", "ast_colour_improvement", "ast_colour_deterioration", "ast_colour_neutral_low", "ast_colour_neutral_high"], + "Shifts": ["shift", "shift_n", "shift_colour_improvement", "shift_colour_deterioration", "shift_colour_neutral_low", "shift_colour_neutral_high"], + "Trends": ["trend", "trend_n", "trend_colour_improvement", "trend_colour_deterioration", "trend_colour_neutral_low", "trend_colour_neutral_high"], + "Two-In-Three": ["two_in_three", "two_in_three_highlight_series", "two_in_three_limit", "twointhree_colour_improvement", "twointhree_colour_deterioration", "twointhree_colour_neutral_low", "twointhree_colour_neutral_high"] }, lines: { "Main": ["show_main", "width_main", "type_main", "colour_main"], diff --git a/src/visual.ts b/src/visual.ts index 2265d99..0a8dea6 100644 --- a/src/visual.ts +++ b/src/visual.ts @@ -7,7 +7,7 @@ import * as d3 from "./D3 Plotting Functions/D3 Modules"; import { drawXAxis, drawYAxis, drawTooltipLine, drawLines, drawDots, drawIcons, updateHighlighting, addContextMenu, drawErrors, initialiseSVG } from "./D3 Plotting Functions" -import { viewModelClass } from "./Classes" +import { defaultSettingsKey, viewModelClass } from "./Classes" import { validateDataView } from "./Functions"; export type svgBaseType = d3.Selection; @@ -79,6 +79,6 @@ export class Visual implements powerbi.extensibility.IVisual { // Function to render the properties specified in capabilities.json to the properties pane public enumerateObjectInstances(options: EnumerateVisualObjectInstancesOptions): VisualObjectInstanceEnumerationObject { - return this.viewModel.inputSettings.createSettingsEntry(options.objectName); + return this.viewModel.inputSettings.createSettingsEntry(options.objectName as defaultSettingsKey); } }