From 8ae149ce4acc3709f02acf9257aa773da212175c Mon Sep 17 00:00:00 2001 From: adiletelf <74559101+adiletelf@users.noreply.github.com> Date: Wed, 31 Jul 2024 14:36:38 +0600 Subject: [PATCH] Add toggle 'Treat as end of fiscal year' (#150) * Add toggle 'Treat as end of fiscal year' * Disable 'Force selection' toggles when filters are changed * Fix 'Force selection' update on filter change * Update packages, bump version to 2.5.6.0 * Fix selecting current period * Fix npm vulnerabilities * Fix forceSelection update when currentPeriod dates is null * Add labels toggle switches to allow users to customize which labels to show * Fix week granularity label Previously it showed 'Year 5 - 2014, Q1 Jan' instead of 'Week 5 - 2014, Q1 Jan' * Fix rendering visual when scroll is enabled. Fix scroll arrows not being clicked * Remove range header font size restrictions * Remove displayAll from labels * Remove tests with label 'displayAll' --- CHANGELOG.md | 12 + capabilities.json | 27 +- package-lock.json | 508 ++++++++---------------- package.json | 22 +- pbiviz.json | 6 +- src/calendars/calendar.ts | 36 +- src/calendars/calendarFactory.ts | 9 +- src/calendars/calendarISO8061.ts | 6 +- src/granularity/weekGranularity.ts | 2 +- src/timeLine.ts | 222 ++++++++--- src/timeLineSettingsModel.ts | 59 ++- stringResources/en-US/resources.resjson | 6 + test/visual.test.ts | 39 +- 13 files changed, 497 insertions(+), 457 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index d16aecb..5bda07a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,15 @@ +## 2.5.7 +* Fix rendering visual when scroll is enabled. Fix scroll arrows not being clicked +* Remove range header font size restrictions + +## 2.5.6 +* Fix ignoring filters when "Force selection" is enabled. "Force selection" will automatically disable if filters select another period +* Add labels toggle switches to allow users to customize which labels to show +* Fix week granularity label. Previously it showed 'Year 5 - 2014, Q1 Jan' instead of 'Week 5 - 2014, Q1 Jan' + +## 2.5.5 +* Add toggle 'Treat as end of fiscal year' + ## 2.5.4 * Disable "Calendar" and "First day of week" settings when WeekStandard is set to IS0 860 * Fix uninitialized start date when date from filters is less than date from DataView diff --git a/capabilities.json b/capabilities.json index 4e4fd97..5e56e6a 100644 --- a/capabilities.json +++ b/capabilities.json @@ -99,6 +99,11 @@ }, "calendar": { "properties": { + "treatAsEndOfFiscalYear": { + "type": { + "bool": true + } + }, "month": { "type": { "enumeration": [ @@ -405,7 +410,27 @@ "bool": true } }, - "displayAll": { + "displayYears": { + "type": { + "bool": true + } + }, + "displayQuarters": { + "type": { + "bool": true + } + }, + "displayMonths": { + "type": { + "bool": true + } + }, + "displayWeeks": { + "type": { + "bool": true + } + }, + "displayDays": { "type": { "bool": true } diff --git a/package-lock.json b/package-lock.json index c5421b3..895ee26 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,16 +1,16 @@ { "name": "powerbi-visuals-timeline", - "version": "2.5.4.0", + "version": "2.5.7.0", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "powerbi-visuals-timeline", - "version": "2.5.4.0", + "version": "2.5.7.0", "license": "MIT", "dependencies": { - "@typescript-eslint/eslint-plugin": "^7.7.1", - "@typescript-eslint/parser": "^7.7.1", + "@typescript-eslint/eslint-plugin": "^7.17.0", + "@typescript-eslint/parser": "^7.17.0", "d3-array": "^3.2.4", "d3-drag": "^3.0.0", "d3-selection": "^3.0.0", @@ -32,9 +32,9 @@ "@types/d3-shape": "^3.1.6", "@types/jasmine": "^5.1.4", "@types/karma": "^6.3.8", - "@types/node": "^20.12.7", - "css-loader": "^7.1.1", - "jasmine": "^5.1.0", + "@types/node": "^20.14.12", + "css-loader": "^7.1.2", + "jasmine": "^5.2.0", "karma": "^6.4.3", "karma-chrome-launcher": "^3.2.0", "karma-coverage": "^2.2.1", @@ -44,14 +44,14 @@ "karma-webpack": "^5.0.1", "less": "^4.2.0", "less-loader": "^12.2.0", - "playwright-chromium": "^1.43.1", - "powerbi-visuals-api": "~5.9.0", - "powerbi-visuals-tools": "^5.4.3", + "playwright-chromium": "^1.45.3", + "powerbi-visuals-api": "~5.11.0", + "powerbi-visuals-tools": "^5.5.1", "powerbi-visuals-utils-testutils": "^6.1.1", "style-loader": "^4.0.0", "ts-loader": "^9.5.1", - "typescript": "^5.4.5", - "webpack": "^5.91.0" + "typescript": "^5.5.4", + "webpack": "^5.93.0" } }, "node_modules/@aashutoshrathi/word-wrap": { @@ -761,9 +761,9 @@ } }, "node_modules/@polka/url": { - "version": "1.0.0-next.24", - "resolved": "https://registry.npmjs.org/@polka/url/-/url-1.0.0-next.24.tgz", - "integrity": "sha512-2LuNTFBIO0m7kKIQvvPHN6UE63VjpmL9rnEEaOOaiSPbZK+zUOYIzBAWcED+3XYzhYsd/0mD57VdxAEqqV52CQ==", + "version": "1.0.0-next.25", + "resolved": "https://registry.npmjs.org/@polka/url/-/url-1.0.0-next.25.tgz", + "integrity": "sha512-j7P6Rgr3mmtdkeDGTe0E/aYyWEWVtc5yFXtHCRHs28/jptDEWfaVOc5T7cblqy1XKPPfCxJc/8DwQ5YgLOZOVQ==", "dev": true }, "node_modules/@socket.io/component-emitter": { @@ -940,7 +940,8 @@ "node_modules/@types/json-schema": { "version": "7.0.15", "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz", - "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==" + "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==", + "dev": true }, "node_modules/@types/karma": { "version": "6.3.8", @@ -959,9 +960,9 @@ "dev": true }, "node_modules/@types/node": { - "version": "20.12.7", - "resolved": "https://registry.npmjs.org/@types/node/-/node-20.12.7.tgz", - "integrity": "sha512-wq0cICSkRLVaf3UGLMGItu/PtdY7oaXaI/RVU+xliKVOtRna3PRY57ZDfztpDL0n11vfymMUnXv8QwYCO7L1wg==", + "version": "20.14.12", + "resolved": "https://registry.npmjs.org/@types/node/-/node-20.14.12.tgz", + "integrity": "sha512-r7wNXakLeSsGT0H1AU863vS2wa5wBOK4bWMjZz2wj+8nBx+m5PeIn0k8AloSLpRuiwdRQZwarZqHE4FNArPuJQ==", "dev": true, "dependencies": { "undici-types": "~5.26.4" @@ -994,11 +995,6 @@ "integrity": "sha512-wWKOClTTiizcZhXnPY4wikVAwmdYHp8q6DmC+EJUzAMsycb7HB32Kh9RN4+0gExjmPmZSAQjgURXIGATPegAvA==", "dev": true }, - "node_modules/@types/semver": { - "version": "7.5.8", - "resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.5.8.tgz", - "integrity": "sha512-I8EUhyrgfLrcTkzV3TSsGyl1tSuPrEDzr0yd5m90UgNxQkyDXULk3b6MlQqTCpZpNtWe1K0hzclnZkTcLBe2UQ==" - }, "node_modules/@types/send": { "version": "0.17.4", "resolved": "https://registry.npmjs.org/@types/send/-/send-0.17.4.tgz", @@ -1048,20 +1044,18 @@ } }, "node_modules/@typescript-eslint/eslint-plugin": { - "version": "7.7.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-7.7.1.tgz", - "integrity": "sha512-KwfdWXJBOviaBVhxO3p5TJiLpNuh2iyXyjmWN0f1nU87pwyvfS0EmjC6ukQVYVFJd/K1+0NWGPDXiyEyQorn0Q==", + "version": "7.17.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-7.17.0.tgz", + "integrity": "sha512-pyiDhEuLM3PuANxH7uNYan1AaFs5XE0zw1hq69JBvGvE7gSuEoQl1ydtEe/XQeoC3GQxLXyOVa5kNOATgM638A==", "dependencies": { "@eslint-community/regexpp": "^4.10.0", - "@typescript-eslint/scope-manager": "7.7.1", - "@typescript-eslint/type-utils": "7.7.1", - "@typescript-eslint/utils": "7.7.1", - "@typescript-eslint/visitor-keys": "7.7.1", - "debug": "^4.3.4", + "@typescript-eslint/scope-manager": "7.17.0", + "@typescript-eslint/type-utils": "7.17.0", + "@typescript-eslint/utils": "7.17.0", + "@typescript-eslint/visitor-keys": "7.17.0", "graphemer": "^1.4.0", "ignore": "^5.3.1", "natural-compare": "^1.4.0", - "semver": "^7.6.0", "ts-api-utils": "^1.3.0" }, "engines": { @@ -1082,14 +1076,14 @@ } }, "node_modules/@typescript-eslint/parser": { - "version": "7.7.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-7.7.1.tgz", - "integrity": "sha512-vmPzBOOtz48F6JAGVS/kZYk4EkXao6iGrD838sp1w3NQQC0W8ry/q641KU4PrG7AKNAf56NOcR8GOpH8l9FPCw==", - "dependencies": { - "@typescript-eslint/scope-manager": "7.7.1", - "@typescript-eslint/types": "7.7.1", - "@typescript-eslint/typescript-estree": "7.7.1", - "@typescript-eslint/visitor-keys": "7.7.1", + "version": "7.17.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-7.17.0.tgz", + "integrity": "sha512-puiYfGeg5Ydop8eusb/Hy1k7QmOU6X3nvsqCgzrB2K4qMavK//21+PzNE8qeECgNOIoertJPUC1SpegHDI515A==", + "dependencies": { + "@typescript-eslint/scope-manager": "7.17.0", + "@typescript-eslint/types": "7.17.0", + "@typescript-eslint/typescript-estree": "7.17.0", + "@typescript-eslint/visitor-keys": "7.17.0", "debug": "^4.3.4" }, "engines": { @@ -1109,12 +1103,12 @@ } }, "node_modules/@typescript-eslint/scope-manager": { - "version": "7.7.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-7.7.1.tgz", - "integrity": "sha512-PytBif2SF+9SpEUKynYn5g1RHFddJUcyynGpztX3l/ik7KmZEv19WCMhUBkHXPU9es/VWGD3/zg3wg90+Dh2rA==", + "version": "7.17.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-7.17.0.tgz", + "integrity": "sha512-0P2jTTqyxWp9HiKLu/Vemr2Rg1Xb5B7uHItdVZ6iAenXmPo4SZ86yOPCJwMqpCyaMiEHTNqizHfsbmCFT1x9SA==", "dependencies": { - "@typescript-eslint/types": "7.7.1", - "@typescript-eslint/visitor-keys": "7.7.1" + "@typescript-eslint/types": "7.17.0", + "@typescript-eslint/visitor-keys": "7.17.0" }, "engines": { "node": "^18.18.0 || >=20.0.0" @@ -1125,12 +1119,12 @@ } }, "node_modules/@typescript-eslint/type-utils": { - "version": "7.7.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-7.7.1.tgz", - "integrity": "sha512-ZksJLW3WF7o75zaBPScdW1Gbkwhd/lyeXGf1kQCxJaOeITscoSl0MjynVvCzuV5boUz/3fOI06Lz8La55mu29Q==", + "version": "7.17.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-7.17.0.tgz", + "integrity": "sha512-XD3aaBt+orgkM/7Cei0XNEm1vwUxQ958AOLALzPlbPqb8C1G8PZK85tND7Jpe69Wualri81PLU+Zc48GVKIMMA==", "dependencies": { - "@typescript-eslint/typescript-estree": "7.7.1", - "@typescript-eslint/utils": "7.7.1", + "@typescript-eslint/typescript-estree": "7.17.0", + "@typescript-eslint/utils": "7.17.0", "debug": "^4.3.4", "ts-api-utils": "^1.3.0" }, @@ -1151,9 +1145,9 @@ } }, "node_modules/@typescript-eslint/types": { - "version": "7.7.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-7.7.1.tgz", - "integrity": "sha512-AmPmnGW1ZLTpWa+/2omPrPfR7BcbUU4oha5VIbSbS1a1Tv966bklvLNXxp3mrbc+P2j4MNOTfDffNsk4o0c6/w==", + "version": "7.17.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-7.17.0.tgz", + "integrity": "sha512-a29Ir0EbyKTKHnZWbNsrc/gqfIBqYPwj3F2M+jWE/9bqfEHg0AMtXzkbUkOG6QgEScxh2+Pz9OXe11jHDnHR7A==", "engines": { "node": "^18.18.0 || >=20.0.0" }, @@ -1163,12 +1157,12 @@ } }, "node_modules/@typescript-eslint/typescript-estree": { - "version": "7.7.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-7.7.1.tgz", - "integrity": "sha512-CXe0JHCXru8Fa36dteXqmH2YxngKJjkQLjxzoj6LYwzZ7qZvgsLSc+eqItCrqIop8Vl2UKoAi0StVWu97FQZIQ==", + "version": "7.17.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-7.17.0.tgz", + "integrity": "sha512-72I3TGq93t2GoSBWI093wmKo0n6/b7O4j9o8U+f65TVD0FS6bI2180X5eGEr8MA8PhKMvYe9myZJquUT2JkCZw==", "dependencies": { - "@typescript-eslint/types": "7.7.1", - "@typescript-eslint/visitor-keys": "7.7.1", + "@typescript-eslint/types": "7.17.0", + "@typescript-eslint/visitor-keys": "7.17.0", "debug": "^4.3.4", "globby": "^11.1.0", "is-glob": "^4.0.3", @@ -1198,9 +1192,9 @@ } }, "node_modules/@typescript-eslint/typescript-estree/node_modules/minimatch": { - "version": "9.0.4", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.4.tgz", - "integrity": "sha512-KqWh+VchfxcMNRAJjj2tnsSJdNbHsVgnkBhTNrW7AjVo6OvLtxw8zfT9oLw1JSohlFzJ8jCoTgaoXvJ+kHt6fw==", + "version": "9.0.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", + "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", "dependencies": { "brace-expansion": "^2.0.1" }, @@ -1212,17 +1206,14 @@ } }, "node_modules/@typescript-eslint/utils": { - "version": "7.7.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-7.7.1.tgz", - "integrity": "sha512-QUvBxPEaBXf41ZBbaidKICgVL8Hin0p6prQDu6bbetWo39BKbWJxRsErOzMNT1rXvTll+J7ChrbmMCXM9rsvOQ==", + "version": "7.17.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-7.17.0.tgz", + "integrity": "sha512-r+JFlm5NdB+JXc7aWWZ3fKSm1gn0pkswEwIYsrGPdsT2GjsRATAKXiNtp3vgAAO1xZhX8alIOEQnNMl3kbTgJw==", "dependencies": { "@eslint-community/eslint-utils": "^4.4.0", - "@types/json-schema": "^7.0.15", - "@types/semver": "^7.5.8", - "@typescript-eslint/scope-manager": "7.7.1", - "@typescript-eslint/types": "7.7.1", - "@typescript-eslint/typescript-estree": "7.7.1", - "semver": "^7.6.0" + "@typescript-eslint/scope-manager": "7.17.0", + "@typescript-eslint/types": "7.17.0", + "@typescript-eslint/typescript-estree": "7.17.0" }, "engines": { "node": "^18.18.0 || >=20.0.0" @@ -1236,11 +1227,11 @@ } }, "node_modules/@typescript-eslint/visitor-keys": { - "version": "7.7.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-7.7.1.tgz", - "integrity": "sha512-gBL3Eq25uADw1LQ9kVpf3hRM+DWzs0uZknHYK3hq4jcTPqVCClHGDnB6UUUV2SFeBeA4KWHWbbLqmbGcZ4FYbw==", + "version": "7.17.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-7.17.0.tgz", + "integrity": "sha512-RVGC9UhPOCsfCdI9pU++K4nD7to+jTcMIbXTSOcrLqUEW6gF2pU1UUbYJKc9cvcRSK1UDeMJ7pdMxf4bhMpV/A==", "dependencies": { - "@typescript-eslint/types": "7.7.1", + "@typescript-eslint/types": "7.17.0", "eslint-visitor-keys": "^3.4.3" }, "engines": { @@ -1450,6 +1441,15 @@ "node": ">=0.4.0" } }, + "node_modules/acorn-import-attributes": { + "version": "1.9.5", + "resolved": "https://registry.npmjs.org/acorn-import-attributes/-/acorn-import-attributes-1.9.5.tgz", + "integrity": "sha512-n02Vykv5uA3eHGM/Z2dQrcD56kL8TyDb2p1+0P83PClMnC/nc+anbQRhIOWnSq4Ke/KvDPrY3C9hDtC/A3eHnQ==", + "dev": true, + "peerDependencies": { + "acorn": "^8" + } + }, "node_modules/acorn-jsx": { "version": "5.3.2", "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", @@ -1459,10 +1459,13 @@ } }, "node_modules/acorn-walk": { - "version": "8.3.2", - "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.3.2.tgz", - "integrity": "sha512-cjkyv4OtNCIeqhHrfS81QWXoCBPExR/J62oyEqepVw8WaQeSqpW2uhuLPh1m9eWhDuOo/jUXVTlifvesOWp/4A==", + "version": "8.3.3", + "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.3.3.tgz", + "integrity": "sha512-MxXdReSRhGO7VlFe1bRG/oI7/mdLV9B9JJT0N8vZOhF7gFRR5l3M8W9G8JxmKV+JC5mGqJ0QvqfSOLsCPa4nUw==", "dev": true, + "dependencies": { + "acorn": "^8.11.0" + }, "engines": { "node": ">=0.4.0" } @@ -1747,11 +1750,11 @@ } }, "node_modules/braces": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", - "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", + "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", "dependencies": { - "fill-range": "^7.0.1" + "fill-range": "^7.1.1" }, "engines": { "node": ">=8" @@ -2396,9 +2399,9 @@ } }, "node_modules/css-loader": { - "version": "7.1.1", - "resolved": "https://registry.npmjs.org/css-loader/-/css-loader-7.1.1.tgz", - "integrity": "sha512-OxIR5P2mjO1PSXk44bWuQ8XtMK4dpEqpIyERCx3ewOo3I8EmbcxMPUc5ScLtQfgXtOojoMv57So4V/C02HQLsw==", + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/css-loader/-/css-loader-7.1.2.tgz", + "integrity": "sha512-6WvYYn7l/XEGN8Xu2vWFt9nVzrCn39vKyTEFf/ExEyoksJjjSZV/0/35XPlMbpnr6VGhZIUg5yJrL8tGfes/FA==", "dev": true, "dependencies": { "icss-utils": "^5.1.0", @@ -2809,9 +2812,9 @@ } }, "node_modules/engine.io": { - "version": "6.5.4", - "resolved": "https://registry.npmjs.org/engine.io/-/engine.io-6.5.4.tgz", - "integrity": "sha512-KdVSDKhVKyOi+r5uEabrDLZw2qXStVvCsEB/LN3mw4WFi6Gx50jTyuxYVCwAAC0U46FdnzP/ScKRBTXb/NiEOg==", + "version": "6.5.5", + "resolved": "https://registry.npmjs.org/engine.io/-/engine.io-6.5.5.tgz", + "integrity": "sha512-C5Pn8Wk+1vKBoHghJODM63yk8MvrO9EWZUfkAt5HAqIgPE4/8FF0PEGHXtEd40l223+cE5ABWuPzm38PHFXfMA==", "dev": true, "dependencies": { "@types/cookie": "^0.4.1", @@ -2823,7 +2826,7 @@ "cors": "~2.8.5", "debug": "~4.3.1", "engine.io-parser": "~5.2.1", - "ws": "~8.11.0" + "ws": "~8.17.1" }, "engines": { "node": ">=10.2.0" @@ -2839,16 +2842,16 @@ } }, "node_modules/engine.io/node_modules/ws": { - "version": "8.11.0", - "resolved": "https://registry.npmjs.org/ws/-/ws-8.11.0.tgz", - "integrity": "sha512-HPG3wQd9sNQoT9xHyNCXoDUa+Xw/VevmY9FoHyQ+g+rrMn4j6FB4np7Z0OhdTgjx6MgQLK7jwSy1YecU1+4Asg==", + "version": "8.17.1", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.17.1.tgz", + "integrity": "sha512-6XQFvXTkbfUOZOKKILFG1PDK2NDQs4azKQl26T0YS5CxqWLgXajbPZ+h4gZekJyRqFU8pvnbAbbs/3TgRPy+GQ==", "dev": true, "engines": { "node": ">=10.0.0" }, "peerDependencies": { "bufferutil": "^4.0.1", - "utf-8-validate": "^5.0.2" + "utf-8-validate": ">=5.0.2" }, "peerDependenciesMeta": { "bufferutil": { @@ -2860,9 +2863,9 @@ } }, "node_modules/enhanced-resolve": { - "version": "5.16.0", - "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.16.0.tgz", - "integrity": "sha512-O+QWCviPNSSLAD9Ucn8Awv+poAkqn3T1XY5/N7kR7rQO9yfSGWkYZDwpJ+iKF7B8rxaQKWngSqACpgzeapSyoA==", + "version": "5.17.0", + "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.17.0.tgz", + "integrity": "sha512-dwDPwZL0dmye8Txp2gzFmA6sxALaSvdRDjPH0viLcKrtlOL3tw62nWWweVD1SdILDTJrbrL6tdWVN58Wo6U3eA==", "dev": true, "dependencies": { "graceful-fs": "^4.2.4", @@ -3405,9 +3408,9 @@ } }, "node_modules/fill-range": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", - "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", + "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", "dependencies": { "to-regex-range": "^5.0.1" }, @@ -4462,22 +4465,22 @@ } }, "node_modules/jasmine": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/jasmine/-/jasmine-5.1.0.tgz", - "integrity": "sha512-prmJlC1dbLhti4nE4XAPDWmfJesYO15sjGXVp7Cs7Ym5I9Xtwa/hUHxxJXjnpfLO72+ySttA0Ztf8g/RiVnUKw==", + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/jasmine/-/jasmine-5.2.0.tgz", + "integrity": "sha512-il+noV96N1BGU9/FMmc8QtAMxC8lPnXUiAvgb0o9MDZATRdxglTQe9wo6UdL049ropQL6MopDYwDlludKR6wJQ==", "dev": true, "dependencies": { "glob": "^10.2.2", - "jasmine-core": "~5.1.0" + "jasmine-core": "~5.2.0" }, "bin": { "jasmine": "bin/jasmine.js" } }, "node_modules/jasmine-core": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/jasmine-core/-/jasmine-core-5.1.2.tgz", - "integrity": "sha512-2oIUMGn00FdUiqz6epiiJr7xcFyNYj3rDcfmnzfkBnHyBQ3cBQUs4mmyGsOb7TTLb9kxk7dBcmEmqhDKkBoDyA==", + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/jasmine-core/-/jasmine-core-5.2.0.tgz", + "integrity": "sha512-tSAtdrvWybZkQmmaIoDgnvHG8ORUNw5kEVlO5CvrXj02Jjr9TZrmjFq7FUiOUzJiOP2wLGYT6PgrQgQF4R1xiw==", "dev": true }, "node_modules/jasmine/node_modules/brace-expansion": { @@ -5225,6 +5228,26 @@ "node": ">=6" } }, + "node_modules/mini-css-extract-plugin": { + "version": "2.9.0", + "resolved": "https://registry.npmjs.org/mini-css-extract-plugin/-/mini-css-extract-plugin-2.9.0.tgz", + "integrity": "sha512-Zs1YsZVfemekSZG+44vBsYTLQORkPMwnlv+aehcxK/NLKC+EGhDB39/YePYYqx/sTk6NnYpuqikhSn7+JIevTA==", + "dev": true, + "dependencies": { + "schema-utils": "^4.0.0", + "tapable": "^2.2.1" + }, + "engines": { + "node": ">= 12.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "webpack": "^5.0.0" + } + }, "node_modules/minimalistic-assert": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz", @@ -5781,31 +5804,31 @@ } }, "node_modules/playwright-chromium": { - "version": "1.43.1", - "resolved": "https://registry.npmjs.org/playwright-chromium/-/playwright-chromium-1.43.1.tgz", - "integrity": "sha512-HL/11hcpIYVt/Rw7wTf5xwm+W0R76JGDDjRxGtBkgMuUqi0Q4UYmx+F/QxW/Z+aApSbDtHd3naRFcL/Jt7Aidg==", + "version": "1.45.3", + "resolved": "https://registry.npmjs.org/playwright-chromium/-/playwright-chromium-1.45.3.tgz", + "integrity": "sha512-GXY/+1HScU5soR06lu8sc7cnXWBnfWDGH2JV+D3wqxApb9qJbSxDK40H8loOok+naxMO7x6yhgPHwpRSCV9dug==", "dev": true, "hasInstallScript": true, "dependencies": { - "playwright-core": "1.43.1" + "playwright-core": "1.45.3" }, "bin": { "playwright": "cli.js" }, "engines": { - "node": ">=16" + "node": ">=18" } }, "node_modules/playwright-core": { - "version": "1.43.1", - "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.43.1.tgz", - "integrity": "sha512-EI36Mto2Vrx6VF7rm708qSnesVQKbxEWvPrfA1IPY6HgczBplDx7ENtx+K2n4kJ41sLLkuGfmb0ZLSSXlDhqPg==", + "version": "1.45.3", + "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.45.3.tgz", + "integrity": "sha512-+ym0jNbcjikaOwwSZycFbwkWgfruWvYlJfThKYAlImbxUgdWFO2oW70ojPm4OpE4t6TAo2FY/smM+hpVTtkhDA==", "dev": true, "bin": { "playwright-core": "cli.js" }, "engines": { - "node": ">=16" + "node": ">=18" } }, "node_modules/possible-typed-array-names": { @@ -5929,20 +5952,20 @@ "integrity": "sha512-UcBLtBB2wa8pL412DxyVZvv/bKiUVJfLTCXAuGmIqnOkoA/EnYDUM3B8oS2piN1azyEN8pjN353rE9DeARCJtA==" }, "node_modules/powerbi-visuals-api": { - "version": "5.9.0", - "resolved": "https://registry.npmjs.org/powerbi-visuals-api/-/powerbi-visuals-api-5.9.0.tgz", - "integrity": "sha512-i7GLJOr2gNvdvHuGqTt+ktBVuRcrduoJMIG94avCba/iPEo2pHGqbdESfsV85UALwZA+6lTKfOnwn83lDh0rCA==", + "version": "5.11.0", + "resolved": "https://registry.npmjs.org/powerbi-visuals-api/-/powerbi-visuals-api-5.11.0.tgz", + "integrity": "sha512-OWvhexc4Dqz4swiQiZLnGj+NWUoX84metePeRw5Or3wG3lN/0x6Yd3xqJwBH67uYyjqQ/eomuF7lvbryxCqTbQ==", "dependencies": { "semver": "^7.6.0" } }, "node_modules/powerbi-visuals-tools": { - "version": "5.4.3", - "resolved": "https://registry.npmjs.org/powerbi-visuals-tools/-/powerbi-visuals-tools-5.4.3.tgz", - "integrity": "sha512-msriNFlq+jk+YfJ7lBOG657QJggFCD3h0YMmmk2H5F7M4doVoVuY/79jcs3MacjZds3lMARKexxR+V6n/tiwgA==", + "version": "5.5.1", + "resolved": "https://registry.npmjs.org/powerbi-visuals-tools/-/powerbi-visuals-tools-5.5.1.tgz", + "integrity": "sha512-330nPyn5KEssbVmYnoKHU0DiyUzxeZrw2tYIo1kaWUqkF7r6CUJuUTy/UNO8t6GGjgaYK6ieBE3ttRJO81nQ8Q==", "dev": true, "dependencies": { - "@typescript-eslint/parser": "^6.21.0", + "@typescript-eslint/parser": "^7.12.0", "assert": "^2.1.0", "async": "^3.2.5", "browserify-zlib": "^0.2.0", @@ -5953,7 +5976,7 @@ "console-browserify": "^1.2.0", "constants-browserify": "^1.0.0", "crypto-browserify": "^3.12.0", - "css-loader": "^6.10.0", + "css-loader": "^6.11.0", "domain-browser": "^5.7.0", "events": "^3.3.0", "extra-watch-webpack-plugin": "^1.0.3", @@ -5963,12 +5986,12 @@ "json-loader": "0.5.7", "jszip": "^3.10.1", "less": "^4.2.0", - "less-loader": "^11.1.4", + "less-loader": "^12.2.0", "lodash.clonedeep": "4.5.0", "lodash.defaults": "4.2.0", "lodash.isequal": "4.5.0", "lodash.ismatch": "^4.4.0", - "mini-css-extract-plugin": "^2.8.1", + "mini-css-extract-plugin": "^2.9.0", "os-browserify": "^0.3.0", "path-browserify": "^1.0.1", "powerbi-visuals-webpack-plugin": "4.1.0", @@ -5988,7 +6011,7 @@ "util": "^0.12.5", "vm-browserify": "^1.1.2", "webpack": "^5.91.0", - "webpack-bundle-analyzer": "4.10.1", + "webpack-bundle-analyzer": "4.10.2", "webpack-dev-server": "^4.15.2" }, "bin": { @@ -6001,109 +6024,6 @@ "fsevents": "*" } }, - "node_modules/powerbi-visuals-tools/node_modules/@typescript-eslint/parser": { - "version": "6.21.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-6.21.0.tgz", - "integrity": "sha512-tbsV1jPne5CkFQCgPBcDOt30ItF7aJoZL997JSF7MhGQqOeT3svWRYxiqlfA5RUdlHN6Fi+EI9bxqbdyAUZjYQ==", - "dev": true, - "dependencies": { - "@typescript-eslint/scope-manager": "6.21.0", - "@typescript-eslint/types": "6.21.0", - "@typescript-eslint/typescript-estree": "6.21.0", - "@typescript-eslint/visitor-keys": "6.21.0", - "debug": "^4.3.4" - }, - "engines": { - "node": "^16.0.0 || >=18.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "eslint": "^7.0.0 || ^8.0.0" - }, - "peerDependenciesMeta": { - "typescript": { - "optional": true - } - } - }, - "node_modules/powerbi-visuals-tools/node_modules/@typescript-eslint/scope-manager": { - "version": "6.21.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-6.21.0.tgz", - "integrity": "sha512-OwLUIWZJry80O99zvqXVEioyniJMa+d2GrqpUTqi5/v5D5rOrppJVBPa0yKCblcigC0/aYAzxxqQ1B+DS2RYsg==", - "dev": true, - "dependencies": { - "@typescript-eslint/types": "6.21.0", - "@typescript-eslint/visitor-keys": "6.21.0" - }, - "engines": { - "node": "^16.0.0 || >=18.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - } - }, - "node_modules/powerbi-visuals-tools/node_modules/@typescript-eslint/types": { - "version": "6.21.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-6.21.0.tgz", - "integrity": "sha512-1kFmZ1rOm5epu9NZEZm1kckCDGj5UJEf7P1kliH4LKu/RkwpsfqqGmY2OOcUs18lSlQBKLDYBOGxRVtrMN5lpg==", - "dev": true, - "engines": { - "node": "^16.0.0 || >=18.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - } - }, - "node_modules/powerbi-visuals-tools/node_modules/@typescript-eslint/typescript-estree": { - "version": "6.21.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-6.21.0.tgz", - "integrity": "sha512-6npJTkZcO+y2/kr+z0hc4HwNfrrP4kNYh57ek7yCNlrBjWQ1Y0OS7jiZTkgumrvkX5HkEKXFZkkdFNkaW2wmUQ==", - "dev": true, - "dependencies": { - "@typescript-eslint/types": "6.21.0", - "@typescript-eslint/visitor-keys": "6.21.0", - "debug": "^4.3.4", - "globby": "^11.1.0", - "is-glob": "^4.0.3", - "minimatch": "9.0.3", - "semver": "^7.5.4", - "ts-api-utils": "^1.0.1" - }, - "engines": { - "node": "^16.0.0 || >=18.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependenciesMeta": { - "typescript": { - "optional": true - } - } - }, - "node_modules/powerbi-visuals-tools/node_modules/@typescript-eslint/visitor-keys": { - "version": "6.21.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-6.21.0.tgz", - "integrity": "sha512-JJtkDduxLi9bivAB+cYOVMtbkqdPOhZ+ZI5LC47MIRrDV4Yn2o+ZnW10Nkmr28xRpSpdJ6Sm42Hjf2+REYXm0A==", - "dev": true, - "dependencies": { - "@typescript-eslint/types": "6.21.0", - "eslint-visitor-keys": "^3.4.1" - }, - "engines": { - "node": "^16.0.0 || >=18.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - } - }, "node_modules/powerbi-visuals-tools/node_modules/assert": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/assert/-/assert-2.1.0.tgz", @@ -6123,15 +6043,6 @@ "integrity": "sha512-baNZyqaaLhyLVKm/DlvdW051MSgO6b8eVfIezl9E5PqWxFgzLm/wQntEW4zOytVburDEr0JlALEpdOFwvErLsg==", "dev": true }, - "node_modules/powerbi-visuals-tools/node_modules/brace-expansion": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", - "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", - "dev": true, - "dependencies": { - "balanced-match": "^1.0.0" - } - }, "node_modules/powerbi-visuals-tools/node_modules/buffer": { "version": "6.0.3", "resolved": "https://registry.npmjs.org/buffer/-/buffer-6.0.3.tgz", @@ -6250,58 +6161,6 @@ "graceful-fs": "^4.1.6" } }, - "node_modules/powerbi-visuals-tools/node_modules/less-loader": { - "version": "11.1.4", - "resolved": "https://registry.npmjs.org/less-loader/-/less-loader-11.1.4.tgz", - "integrity": "sha512-6/GrYaB6QcW6Vj+/9ZPgKKs6G10YZai/l/eJ4SLwbzqNTBsAqt5hSLVF47TgsiBxV1P6eAU0GYRH3YRuQU9V3A==", - "dev": true, - "engines": { - "node": ">= 14.15.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/webpack" - }, - "peerDependencies": { - "less": "^3.5.0 || ^4.0.0", - "webpack": "^5.0.0" - } - }, - "node_modules/powerbi-visuals-tools/node_modules/mini-css-extract-plugin": { - "version": "2.8.1", - "resolved": "https://registry.npmjs.org/mini-css-extract-plugin/-/mini-css-extract-plugin-2.8.1.tgz", - "integrity": "sha512-/1HDlyFRxWIZPI1ZpgqlZ8jMw/1Dp/dl3P0L1jtZ+zVcHqwPhGwaJwKL00WVgfnBy6PWCde9W65or7IIETImuA==", - "dev": true, - "dependencies": { - "schema-utils": "^4.0.0", - "tapable": "^2.2.1" - }, - "engines": { - "node": ">= 12.13.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/webpack" - }, - "peerDependencies": { - "webpack": "^5.0.0" - } - }, - "node_modules/powerbi-visuals-tools/node_modules/minimatch": { - "version": "9.0.3", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.3.tgz", - "integrity": "sha512-RHiac9mvaRw0x3AYRgDC1CxAP7HTcNrrECeA8YYJeWnpo+2Q5CegtZjaotWTWxDG3UeGA1coE05iH1mPjT/2mg==", - "dev": true, - "dependencies": { - "brace-expansion": "^2.0.1" - }, - "engines": { - "node": ">=16 || 14 >=14.17" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, "node_modules/powerbi-visuals-tools/node_modules/path-browserify": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/path-browserify/-/path-browserify-1.0.1.tgz", @@ -7395,26 +7254,26 @@ } }, "node_modules/socket.io-adapter": { - "version": "2.5.4", - "resolved": "https://registry.npmjs.org/socket.io-adapter/-/socket.io-adapter-2.5.4.tgz", - "integrity": "sha512-wDNHGXGewWAjQPt3pyeYBtpWSq9cLE5UW1ZUPL/2eGK9jtse/FpXib7epSTsz0Q0m+6sg6Y4KtcFTlah1bdOVg==", + "version": "2.5.5", + "resolved": "https://registry.npmjs.org/socket.io-adapter/-/socket.io-adapter-2.5.5.tgz", + "integrity": "sha512-eLDQas5dzPgOWCk9GuuJC2lBqItuhKI4uxGgo9aIV7MYbk2h9Q6uULEh8WBzThoI7l+qU9Ast9fVUmkqPP9wYg==", "dev": true, "dependencies": { "debug": "~4.3.4", - "ws": "~8.11.0" + "ws": "~8.17.1" } }, "node_modules/socket.io-adapter/node_modules/ws": { - "version": "8.11.0", - "resolved": "https://registry.npmjs.org/ws/-/ws-8.11.0.tgz", - "integrity": "sha512-HPG3wQd9sNQoT9xHyNCXoDUa+Xw/VevmY9FoHyQ+g+rrMn4j6FB4np7Z0OhdTgjx6MgQLK7jwSy1YecU1+4Asg==", + "version": "8.17.1", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.17.1.tgz", + "integrity": "sha512-6XQFvXTkbfUOZOKKILFG1PDK2NDQs4azKQl26T0YS5CxqWLgXajbPZ+h4gZekJyRqFU8pvnbAbbs/3TgRPy+GQ==", "dev": true, "engines": { "node": ">=10.0.0" }, "peerDependencies": { "bufferutil": "^4.0.1", - "utf-8-validate": "^5.0.2" + "utf-8-validate": ">=5.0.2" }, "peerDependenciesMeta": { "bufferutil": { @@ -7930,9 +7789,9 @@ } }, "node_modules/typescript": { - "version": "5.4.5", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.4.5.tgz", - "integrity": "sha512-vcI4UpRgg81oIRUFwR0WSIHKt11nJ7SAVlYNIu+QpqeyXP+gpQJy/Z4+F0aGxSE4MqwjyXvW/TzgkLAx2AGHwQ==", + "version": "5.5.4", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.5.4.tgz", + "integrity": "sha512-Mtq29sKDAEYP7aljRgtPOpTvOfbwRWlS6dPRzwjdE+C0R4brX/GUyhHSecbHMFLNBLcJIPt9nl9yG5TZ1weH+Q==", "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" @@ -8128,9 +7987,9 @@ } }, "node_modules/webpack": { - "version": "5.91.0", - "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.91.0.tgz", - "integrity": "sha512-rzVwlLeBWHJbmgTC/8TvAcu5vpJNII+MelQpylD4jNERPwpBJOE2lEcko1zJX3QJeLjTTAnQxn/OJ8bjDzVQaw==", + "version": "5.93.0", + "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.93.0.tgz", + "integrity": "sha512-Y0m5oEY1LRuwly578VqluorkXbvXKh7U3rLoQCEO04M97ScRr44afGVkI0FQFsXzysk5OgFAxjZAb9rsGQVihA==", "dev": true, "dependencies": { "@types/eslint-scope": "^3.7.3", @@ -8139,10 +7998,10 @@ "@webassemblyjs/wasm-edit": "^1.12.1", "@webassemblyjs/wasm-parser": "^1.12.1", "acorn": "^8.7.1", - "acorn-import-assertions": "^1.9.0", + "acorn-import-attributes": "^1.9.5", "browserslist": "^4.21.10", "chrome-trace-event": "^1.0.2", - "enhanced-resolve": "^5.16.0", + "enhanced-resolve": "^5.17.0", "es-module-lexer": "^1.2.1", "eslint-scope": "5.1.1", "events": "^3.2.0", @@ -8175,9 +8034,9 @@ } }, "node_modules/webpack-bundle-analyzer": { - "version": "4.10.1", - "resolved": "https://registry.npmjs.org/webpack-bundle-analyzer/-/webpack-bundle-analyzer-4.10.1.tgz", - "integrity": "sha512-s3P7pgexgT/HTUSYgxJyn28A+99mmLq4HsJepMPzu0R8ImJc52QNqaFYW1Z2z2uIb1/J3eYgaAWVpaC+v/1aAQ==", + "version": "4.10.2", + "resolved": "https://registry.npmjs.org/webpack-bundle-analyzer/-/webpack-bundle-analyzer-4.10.2.tgz", + "integrity": "sha512-vJptkMm9pk5si4Bv922ZbKLV8UTT4zib4FPgXMhgzUny0bfDDkLXAVQs3ly3fS4/TN9ROFtb0NFrm04UXFE/Vw==", "dev": true, "dependencies": { "@discoveryjs/json-ext": "0.5.7", @@ -8188,7 +8047,6 @@ "escape-string-regexp": "^4.0.0", "gzip-size": "^6.0.0", "html-escaper": "^2.0.2", - "is-plain-object": "^5.0.0", "opener": "^1.5.2", "picocolors": "^1.0.0", "sirv": "^2.0.3", @@ -8222,15 +8080,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/webpack-bundle-analyzer/node_modules/is-plain-object": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-5.0.0.tgz", - "integrity": "sha512-VRSzKkbMm5jMDoKLbltAkFQ5Qr7VDiTFGXxYFXXowVj387GeGNOCsOH6Msy00SGZ3Fp84b1Naa1psqgcCIEP5Q==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/webpack-dev-middleware": { "version": "5.3.4", "resolved": "https://registry.npmjs.org/webpack-dev-middleware/-/webpack-dev-middleware-5.3.4.tgz", @@ -8314,9 +8163,9 @@ } }, "node_modules/webpack-dev-server/node_modules/ws": { - "version": "8.16.0", - "resolved": "https://registry.npmjs.org/ws/-/ws-8.16.0.tgz", - "integrity": "sha512-HS0c//TP7Ina87TfiPUz1rQzMhHrl/SG2guqRcTOIUYD2q8uhUdNHZYJUaQ8aTGPzCh+c6oawMKW35nFl1dxyQ==", + "version": "8.18.0", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.18.0.tgz", + "integrity": "sha512-8VbfWfHLbbwu3+N6OKsOMpBdT4kXPDDB9cJk2bJ6mh9ucxdlnNvH1e+roYkKmN9Nxw2yjz7VzeO9oOz2zJ04Pw==", "dev": true, "engines": { "node": ">=10.0.0" @@ -8352,15 +8201,6 @@ "node": ">=10.13.0" } }, - "node_modules/webpack/node_modules/acorn-import-assertions": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/acorn-import-assertions/-/acorn-import-assertions-1.9.0.tgz", - "integrity": "sha512-cmMwop9x+8KFhxvKrKfPYmN6/pKTYYHBqLa0DfvVZcKMJWNyWLnaqND7dx/qn66R7ewM1UX5XMaDVP5wlVTaVA==", - "dev": true, - "peerDependencies": { - "acorn": "^8" - } - }, "node_modules/webpack/node_modules/schema-utils": { "version": "3.3.0", "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.3.0.tgz", @@ -8582,9 +8422,9 @@ "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=" }, "node_modules/ws": { - "version": "7.5.9", - "resolved": "https://registry.npmjs.org/ws/-/ws-7.5.9.tgz", - "integrity": "sha512-F+P9Jil7UiSKSkppIiD94dN07AwvFixvLIj1Og1Rl9GGMuNipJnV9JzjD6XuqmAeiswGvUmNLjr5cFuXwNS77Q==", + "version": "7.5.10", + "resolved": "https://registry.npmjs.org/ws/-/ws-7.5.10.tgz", + "integrity": "sha512-+dbF1tHwZpXcbOJdVOkzLDxZP1ailvSxM6ZweXTegylPny803bFhA+vqBYw4s31NSAk4S2Qz+AKXK9a4wkdjcQ==", "dev": true, "engines": { "node": ">=8.3.0" diff --git a/package.json b/package.json index 2bc2c8c..25e4ae0 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "powerbi-visuals-timeline", - "version": "2.5.4.0", + "version": "2.5.7.0", "description": "Timeline slicer is a graphical date range selector used as a filtering component in the report canvas", "repository": { "type": "git", @@ -34,9 +34,9 @@ "@types/d3-shape": "^3.1.6", "@types/jasmine": "^5.1.4", "@types/karma": "^6.3.8", - "@types/node": "^20.12.7", - "css-loader": "^7.1.1", - "jasmine": "^5.1.0", + "@types/node": "^20.14.12", + "css-loader": "^7.1.2", + "jasmine": "^5.2.0", "karma": "^6.4.3", "karma-chrome-launcher": "^3.2.0", "karma-coverage": "^2.2.1", @@ -46,18 +46,18 @@ "karma-webpack": "^5.0.1", "less": "^4.2.0", "less-loader": "^12.2.0", - "playwright-chromium": "^1.43.1", - "powerbi-visuals-api": "~5.9.0", - "powerbi-visuals-tools": "^5.4.3", + "playwright-chromium": "^1.45.3", + "powerbi-visuals-api": "~5.11.0", + "powerbi-visuals-tools": "^5.5.1", "powerbi-visuals-utils-testutils": "^6.1.1", "style-loader": "^4.0.0", "ts-loader": "^9.5.1", - "typescript": "^5.4.5", - "webpack": "^5.91.0" + "typescript": "^5.5.4", + "webpack": "^5.93.0" }, "dependencies": { - "@typescript-eslint/eslint-plugin": "^7.7.1", - "@typescript-eslint/parser": "^7.7.1", + "@typescript-eslint/eslint-plugin": "^7.17.0", + "@typescript-eslint/parser": "^7.17.0", "d3-array": "^3.2.4", "d3-drag": "^3.0.0", "d3-selection": "^3.0.0", diff --git a/pbiviz.json b/pbiviz.json index e0515cf..221f94c 100644 --- a/pbiviz.json +++ b/pbiviz.json @@ -1,15 +1,15 @@ { "visual": { "name": "Timeline", - "displayName": "Timeline 2.5.4.0", + "displayName": "Timeline 2.5.7.0", "guid": "Timeline1447991079100", "visualClassName": "Timeline", - "version": "2.5.4.0", + "version": "2.5.7.0", "description": "Timeline slicer is a graphical date range selector used as a filtering component in the report canvas", "supportUrl": "https://community.powerbi.com", "gitHubUrl": "https://github.com/Microsoft/powerbi-visuals-timeline" }, - "apiVersion": "5.9.0", + "apiVersion": "5.11.0", "author": { "name": "Microsoft", "email": "pbicvsupport@microsoft.com" diff --git a/src/calendars/calendar.ts b/src/calendars/calendar.ts index 86178c4..9d07213 100644 --- a/src/calendars/calendar.ts +++ b/src/calendars/calendar.ts @@ -47,8 +47,13 @@ export interface WeekdayFormat { day: number; } +export interface CalendarFormattingSettings { + treatAsEndOfFiscalYear: boolean; +} + export class Calendar { private static QuarterFirstMonths: number[] = [0, 3, 6, 9]; + private settings: CalendarFormattingSettings; protected firstDayOfWeek: number; protected firstMonthOfYear: number; @@ -60,7 +65,8 @@ export class Calendar { protected EmptyYearOffset: number = 0; protected YearOffset: number = 1; - constructor(calendarFormat: CalendarFormat, weekDaySettings: WeekdayFormat) { + constructor(calendarFormat: CalendarFormat, weekDaySettings: WeekdayFormat, settings: CalendarFormattingSettings) { + this.settings = settings; this.isDaySelection = weekDaySettings.daySelection; this.firstDayOfWeek = weekDaySettings.day; this.firstMonthOfYear = calendarFormat.month; @@ -78,7 +84,11 @@ export class Calendar { const firstMonthOfYear = this.getFirstMonthOfYear(); const firstDayOfYear = this.getFirstDayOfYear(); - return ((firstMonthOfYear === 0 && firstDayOfYear === 1) ? 0 : 1); + if (firstMonthOfYear === 0 && firstDayOfYear === 1) { + return 0; + } + + return this.settings.treatAsEndOfFiscalYear ? 1 : -1; } public determineYear(date: Date): number { @@ -91,9 +101,18 @@ export class Calendar { firstDayOfYear, ); - return date.getFullYear() + this.getFiscalYearAdjustment() - ((firstDate <= date) - ? this.EmptyYearOffset - : this.YearOffset); + let adjustment: number = this.getFiscalYearAdjustment(); + + if (adjustment === 0) { + return date.getFullYear(); + } + else if (this.settings.treatAsEndOfFiscalYear) { + adjustment -= ((firstDate <= date) ? this.EmptyYearOffset : this.YearOffset); + } else { + adjustment += ((firstDate > date) ? this.EmptyYearOffset : this.YearOffset); + } + + return date.getFullYear() + adjustment; } public determineWeek(date: Date): number[] { @@ -101,7 +120,12 @@ export class Calendar { // It's Ok until this year is used to calculate date of first week. // So, here is some adjustment was applied. const year: number = this.determineYear(date); - const fiscalYearAdjustment = this.getFiscalYearAdjustment(); + let fiscalYearAdjustment = this.getFiscalYearAdjustment(); + + // fiscal year starts with W1 (week 1), meaning previous week should be W52-53 + if (fiscalYearAdjustment === -1) { + fiscalYearAdjustment = 0; + } const dateOfFirstWeek: Date = this.getDateOfFirstWeek(year - fiscalYearAdjustment); const dateOfFirstFullWeek: Date = this.getDateOfFirstFullWeek(year - fiscalYearAdjustment); diff --git a/src/calendars/calendarFactory.ts b/src/calendars/calendarFactory.ts index 608afd8..e9d9095 100644 --- a/src/calendars/calendarFactory.ts +++ b/src/calendars/calendarFactory.ts @@ -1,4 +1,4 @@ -import {Calendar, CalendarFormat, WeekdayFormat} from "./calendar"; +import {Calendar, CalendarFormat, CalendarFormattingSettings, WeekdayFormat} from "./calendar"; import { WeekStandard } from "./weekStandard"; import { CalendarISO8061 } from "./calendarISO8061"; @@ -6,16 +6,17 @@ export class CalendarFactory { public create( weekStandard: WeekStandard, calendarSettings: CalendarFormat, - weekDaySettings: WeekdayFormat) : Calendar { + weekDaySettings: WeekdayFormat, + settings: CalendarFormattingSettings) : Calendar { let calendar: Calendar; switch (weekStandard) { case WeekStandard.ISO8061: - calendar = new CalendarISO8061(); + calendar = new CalendarISO8061(settings); break; default: - calendar = new Calendar(calendarSettings, weekDaySettings) + calendar = new Calendar(calendarSettings, weekDaySettings, settings) } return calendar; diff --git a/src/calendars/calendarISO8061.ts b/src/calendars/calendarISO8061.ts index 6baff72..ec8b218 100644 --- a/src/calendars/calendarISO8061.ts +++ b/src/calendars/calendarISO8061.ts @@ -1,11 +1,11 @@ -import {Calendar, CalendarFormat, WeekdayFormat} from "./calendar"; +import {Calendar, CalendarFormat, CalendarFormattingSettings, WeekdayFormat} from "./calendar"; import { WeekStandard } from "./weekStandard"; import { Utils } from "../utils"; import {CalendarSettingsCard} from "../timeLineSettingsModel"; export class CalendarISO8061 extends Calendar { - constructor() { + constructor(settings: CalendarFormattingSettings) { const isoCalendarSettings: CalendarFormat = { month: CalendarSettingsCard.DefaultMonth, day: CalendarSettingsCard.DefaultDay, @@ -16,7 +16,7 @@ export class CalendarISO8061 extends Calendar { day: 1, }; - super(isoCalendarSettings, isoWeekDaySettings); + super(isoCalendarSettings, isoWeekDaySettings, settings); //this.firstDayOfYear = calendarFormat.day; } diff --git a/src/granularity/weekGranularity.ts b/src/granularity/weekGranularity.ts index 100a677..88fd0a7 100644 --- a/src/granularity/weekGranularity.ts +++ b/src/granularity/weekGranularity.ts @@ -36,7 +36,7 @@ import { IGranularityRenderProps } from "./granularityRenderProps"; import { GranularityType } from "./granularityType"; export class WeekGranularity extends GranularityBase { - private localizationKey: string = "Visual_Granularity_Year"; + private localizationKey: string = "Visual_Granularity_Week"; constructor( calendar: Calendar, diff --git a/src/timeLine.ts b/src/timeLine.ts index 1fbd3f1..163c561 100644 --- a/src/timeLine.ts +++ b/src/timeLine.ts @@ -35,7 +35,7 @@ import {arc as d3Arc} from "d3-shape"; import powerbiVisualsApi from "powerbi-visuals-api"; import powerbi from "powerbi-visuals-api"; -import {AdvancedFilter, IFilterColumnTarget,} from "powerbi-models"; +import {AdvancedFilter, IAdvancedFilterCondition, IFilterColumnTarget,} from "powerbi-models"; import {CssConstants, manipulation as svgManipulation,} from "powerbi-visuals-utils-svgutils"; @@ -66,7 +66,7 @@ import {ITimelineDatePeriod, ITimelineDatePeriodBase,} from "./datePeriod/datePe import {DatePeriodBase} from "./datePeriod/datePeriodBase"; -import {Calendar, CalendarFormat, WeekdayFormat} from "./calendars/calendar"; +import {Calendar, CalendarFormat, CalendarFormattingSettings, WeekdayFormat} from "./calendars/calendar"; import {Utils} from "./utils"; import {WeekStandard} from "./calendars/weekStandard"; import {CalendarFactory} from "./calendars/calendarFactory"; @@ -79,7 +79,7 @@ import { import {FormattingSettingsService} from "powerbi-visuals-utils-formattingmodel"; import ISelectionManager = powerbiVisualsApi.extensibility.ISelectionManager; import extractFilterColumnTarget = interactivityFilterService.extractFilterColumnTarget; -import {Month} from "./calendars/month"; +import { Month } from './calendars/month'; import {Weekday} from "./calendars/weekday"; import {Behavior} from "./behavior"; @@ -106,7 +106,8 @@ export class Timeline implements powerbiVisualsApi.extensibility.visual.IVisual granularity: GranularityType, calendar: Calendar, ) { - return this.SELECT_PERIOD(datePeriod, granularity, calendar, Utils.RESET_TIME(new Date())); + const currentDate: Date = Utils.RESET_TIME(new Date()); + return this.SELECT_PERIOD(datePeriod, granularity, calendar, currentDate); } public CONVERTER( @@ -150,12 +151,11 @@ export class Timeline implements powerbiVisualsApi.extensibility.visual.IVisual } if (!initialized || isCalendarChanged) { - calendar = new CalendarFactory().create(weekStandard, calendarFormat, weekDayFormat); - const granularity: GranularityType = this.visualSettings.granularity.granularity.value - ? this.visualSettings.granularity.granularity.value.value - : GranularityType.month; + const calendarFormattingSettings: CalendarFormattingSettings = { treatAsEndOfFiscalYear: this.visualSettings.calendar.treatAsEndOfFiscalYear.value }; - this.timelineData.currentGranularity = this.timelineGranularityData.getGranularity(granularity); + calendar = new CalendarFactory().create(weekStandard, calendarFormat, weekDayFormat, calendarFormattingSettings); + const granularity: GranularityType = this.getGranularityType(); + this.timelineData.currentGranularity = this.timelineGranularityData.getGranularity(granularity); } else { calendar = previousCalendar; @@ -440,7 +440,7 @@ export class Timeline implements powerbiVisualsApi.extensibility.visual.IVisual const labelSize: number = pixelConverter.fromPointToPixel(this.visualSettings.labels.textSize.value); if (this.visualSettings.labels.show.value) { - const granularityOffset: number = this.visualSettings.labels.displayAll.value ? granularityType + 1 : 1; + const granularityOffset: number = this.visualSettings.labels.show.value ? granularityType + 1 : 1; this.timelineProperties.cellsYPosition += labelSize * Timeline.LabelSizeFactor @@ -621,8 +621,7 @@ export class Timeline implements powerbiVisualsApi.extensibility.visual.IVisual this.headerSelection = this.headerWrapperSelection .append("svg") .attr("width", "100%") - .style("display", "block") - .style("position", "absolute"); + .style("display", "block"); this.mainSvgWrapperSelection = this.rootSelection .append("div") @@ -663,6 +662,7 @@ export class Timeline implements powerbiVisualsApi.extensibility.visual.IVisual } } + // eslint-disable-next-line max-lines-per-function public update(options: powerbiVisualsApi.extensibility.visual.VisualUpdateOptions): void { try { this.host.eventService.renderingStarted(options); @@ -690,12 +690,12 @@ export class Timeline implements powerbiVisualsApi.extensibility.visual.IVisual this.parseJsonFilters(this.visualSettings, (this.options.jsonFilters)); this.setHighContrastColors(); + this.adjustHeightOfElements(options.viewport.width); this.timelineGranularityData = new GranularityData(this.datePeriod.startDate, this.datePeriod.endDate); this.createTimelineData( - this.visualSettings, this.datePeriod.startDate, this.datePeriod.endDate, this.timelineGranularityData, @@ -705,39 +705,16 @@ export class Timeline implements powerbiVisualsApi.extensibility.visual.IVisual this.updateCalendar(); + const shouldRefresh: boolean = this.updateForceSelectionOnFilterChange(); + if (shouldRefresh) { + return; + } + const adjustedPeriod: IAdjustedFilterDatePeriod = this.adjustFilterDatePeriod(); const datePeriod: ITimelineDatePeriodBase = this.datePeriod; - const granularity: GranularityType = this.visualSettings.granularity.granularity.value - ? this.visualSettings.granularity.granularity.value.value - : GranularityType.month; - - const isCurrentPeriodSelected: boolean = !this.isForceSelectionReset && this.visualSettings.forceSelection.currentPeriod.value; - const isLatestAvailableDateSelected: boolean = !this.isForceSelectionReset && this.visualSettings.forceSelection.latestAvailableDate.value; - const isForceSelected: boolean = !this.isForceSelectionReset && (isCurrentPeriodSelected || isLatestAvailableDateSelected); - this.isForceSelectionReset = false; // Reset it to default state to allow re-enabling Force Selection - let currentForceSelectionResult = { startDate: null, endDate: null }; - - if (isCurrentPeriodSelected) { - currentForceSelectionResult = ({ - endDate: adjustedPeriod.period.endDate, - startDate: adjustedPeriod.period.startDate, - } = Timeline.SELECT_CURRENT_PERIOD(datePeriod, granularity, this.calendar)); - } - if (isLatestAvailableDateSelected - && ( - !isCurrentPeriodSelected - || (isCurrentPeriodSelected - && !currentForceSelectionResult.startDate - && !currentForceSelectionResult.endDate - ) - ) - ) { - adjustedPeriod.period.endDate = adjustedPeriod.adaptedDataEndDate; - ({ - endDate: adjustedPeriod.period.endDate, - startDate: adjustedPeriod.period.startDate, - } = Timeline.SELECT_PERIOD(datePeriod, granularity, this.calendar, this.datePeriod.endDate)); - } + const granularity: GranularityType = this.getGranularityType(); + + const isForceSelected: boolean = this.updateDatePeriodOnForceSelection(adjustedPeriod, datePeriod, granularity); this.updatePrevFilterState(adjustedPeriod, isForceSelected, this.timelineData.filterColumnTarget); @@ -782,6 +759,96 @@ export class Timeline implements powerbiVisualsApi.extensibility.visual.IVisual this.host.eventService.renderingFinished(options); } + + /** + * When visual is initialized, we need to check if filter date is different from currentPeriodDate or latestAvailableDate + * It may happen when visual is synced with other visuals and filter date is changed, so we need to disable corresponding forceSelection options. + */ + private updateForceSelectionOnFilterChange(): boolean { + const wasFilterChanged: boolean = + String(this.prevFilteredStartDate) !== String(this.datePeriod.startDate) || + String(this.prevFilteredEndDate) !== String(this.datePeriod.endDate); + + if (!wasFilterChanged) return; + + const filterDatePeriod: DatePeriodBase = this.filterDatePeriod; + const granularity: GranularityType = this.getGranularityType(); + const latestPeriod = Timeline.SELECT_PERIOD(this.datePeriod, granularity, this.calendar, this.datePeriod.endDate); + const currentPeriod = Timeline.SELECT_CURRENT_PERIOD(this.datePeriod, granularity, this.calendar); + + const propertiesToUpdate: Record = {}; + + // TODO: Consider how to make checking of currentPeriod more reliable when filter date is close to midnight + // currentPeriod is created and compared to filter date period. + // If filter date is different from current period then it means we need to disable forceSelection.currentPeriod toggle switch + // currentPeriod resets time and only considers date, so there's a possibility of a bug when time is close to midnight (23:59:59) + // so if filter is created close to midnight and current period is created after midnight, then it will be considered as different + // therefore occasionaly we will disable currentPeriod toggle switch when it's not necessary + if (this.visualSettings.forceSelection.currentPeriod.value && + filterDatePeriod.startDate && + filterDatePeriod.endDate && + currentPeriod.startDate && + currentPeriod.endDate && + currentPeriod.startDate.getTime() !== filterDatePeriod.startDate.getTime() && + currentPeriod.endDate.getTime() !== filterDatePeriod.endDate.getTime() && + this.prevFilteredStartDate == null && + this.prevFilteredEndDate == null + ) { + propertiesToUpdate.currentPeriod = false; + } + + if (this.visualSettings.forceSelection.latestAvailableDate.value && + filterDatePeriod.endDate && + latestPeriod.endDate && + filterDatePeriod.endDate.getTime() !== latestPeriod.endDate.getTime() && + this.prevFilteredEndDate == null + ) { + propertiesToUpdate.latestAvailableDate = false; + } + + if (Object.keys(propertiesToUpdate).length > 0) { + this.host.persistProperties({ + merge: [{ + objectName: "forceSelection", + properties: propertiesToUpdate, + selector: null, + }] + }); + + return true; + } + + return false; + } + + private updateDatePeriodOnForceSelection(adjustedPeriod: IAdjustedFilterDatePeriod, datePeriod: ITimelineDatePeriodBase, granularity: GranularityType) { + const isCurrentPeriodSelected: boolean = !this.isForceSelectionReset && this.visualSettings.forceSelection.currentPeriod.value; + const isLatestAvailableDateSelected: boolean = !this.isForceSelectionReset && this.visualSettings.forceSelection.latestAvailableDate.value; + const isForceSelected: boolean = !this.isForceSelectionReset && (isCurrentPeriodSelected || isLatestAvailableDateSelected); + this.isForceSelectionReset = false; // Reset it to default state to allow re-enabling Force Selection + + if (isCurrentPeriodSelected) { + const currentPeriod = Timeline.SELECT_CURRENT_PERIOD(datePeriod, granularity, this.calendar); + adjustedPeriod.period.startDate = currentPeriod.startDate; + adjustedPeriod.period.endDate = currentPeriod.endDate; + } + + if (isLatestAvailableDateSelected + && ( + !isCurrentPeriodSelected + || (isCurrentPeriodSelected + && !adjustedPeriod.period.startDate + && !adjustedPeriod.period.endDate + ) + )) { + const latestAvailablePeriod = Timeline.SELECT_PERIOD(datePeriod, granularity, this.calendar, this.datePeriod.endDate); + adjustedPeriod.period.startDate = latestAvailablePeriod.startDate; + adjustedPeriod.period.endDate = latestAvailablePeriod.endDate; + } + + return isForceSelected; + } + public fillCells(visSettings: TimeLineSettingsModel): void { const dataPoints: ITimelineDataPoint[] = this.timelineData.timelineDataPoints; @@ -995,9 +1062,7 @@ export class Timeline implements powerbiVisualsApi.extensibility.visual.IVisual return null; } - return new AdvancedFilter( - target, - "And", + const conditions: IAdvancedFilterCondition[] = [ { operator: "GreaterThanOrEqual", value: startDate.toJSON(), @@ -1006,7 +1071,9 @@ export class Timeline implements powerbiVisualsApi.extensibility.visual.IVisual operator: "LessThan", value: endDate.toJSON(), }, - ); + ]; + + return new AdvancedFilter(target, "And", conditions); } public clearSelection(target: IFilterColumnTarget): void { @@ -1023,6 +1090,36 @@ export class Timeline implements powerbiVisualsApi.extensibility.visual.IVisual this.visualSettings.calendar.disabled = true; } + const granularity = this.getGranularityType(); + + switch (granularity) { + case GranularityType.year: + this.visualSettings.labels.displayQuarters.visible = false; + this.visualSettings.labels.displayMonths.visible = false; + this.visualSettings.labels.displayWeeks.visible = false; + this.visualSettings.labels.displayDays.visible = false; + break; + case GranularityType.quarter: + this.visualSettings.labels.displayMonths.visible = false; + this.visualSettings.labels.displayWeeks.visible = false; + this.visualSettings.labels.displayDays.visible = false; + break; + case GranularityType.month: + this.visualSettings.labels.displayWeeks.visible = false; + this.visualSettings.labels.displayDays.visible = false; + break; + case GranularityType.week: + this.visualSettings.labels.displayDays.visible = false; + break; + default: + this.visualSettings.labels.displayMonths.visible = true; + this.visualSettings.labels.displayQuarters.visible = true; + this.visualSettings.labels.displayMonths.visible = true; + this.visualSettings.labels.displayWeeks.visible = true; + this.visualSettings.labels.displayDays.visible = true; + break; + } + return this.formattingSettingsService.buildFormattingModel(this.visualSettings); } @@ -1146,7 +1243,7 @@ export class Timeline implements powerbiVisualsApi.extensibility.visual.IVisual } if (filterDatePeriod.endDate && adaptedDataEndDate && filterDatePeriod.endDate.getTime() > adaptedDataEndDate.getTime()) { - filterDatePeriod.endDate = null; + filterDatePeriod.endDate = adaptedDataEndDate; } return { @@ -1225,16 +1322,16 @@ export class Timeline implements powerbiVisualsApi.extensibility.visual.IVisual } private createTimelineData( - timelineSettings: TimeLineSettingsModel, startDate: Date, endDate: Date, timelineGranularityData: GranularityData, locale: string, localizationManager: powerbiVisualsApi.extensibility.ILocalizationManager, ) { - const { weekStandard, calendarFormat, weekDayFormat } = Timeline.computeCalendarFormat(timelineSettings); + const { weekStandard, calendarFormat, weekDayFormat } = Timeline.computeCalendarFormat(this.visualSettings); - const calendar: Calendar = this.calendarFactory.create(weekStandard, calendarFormat, weekDayFormat); + const calendarFormattingSettings: CalendarFormattingSettings = { treatAsEndOfFiscalYear: this.visualSettings.calendar.treatAsEndOfFiscalYear.value }; + const calendar: Calendar = this.calendarFactory.create(weekStandard, calendarFormat, weekDayFormat, calendarFormattingSettings); timelineGranularityData.createGranularities(calendar, locale, localizationManager); timelineGranularityData.createLabels(); @@ -1255,9 +1352,7 @@ export class Timeline implements powerbiVisualsApi.extensibility.visual.IVisual || actualEndDate.getTime() !== prevEndDate.getTime(); - const granularityType: GranularityType = timelineSettings.granularity.granularity.value - ? timelineSettings.granularity.granularity.value.value - : GranularityType.month; + const granularityType: GranularityType = this.getGranularityType(); if (!changedSelection) { this.changeGranularity( @@ -1382,7 +1477,7 @@ export class Timeline implements powerbiVisualsApi.extensibility.visual.IVisual let yPos: number = 0; if (settings.labels.show.value) { - if (settings.labels.displayAll.value || granularityType === GranularityType.year) { + if (settings.labels.displayYears.value || granularityType === GranularityType.year) { this.renderLabels( extendedLabels.yearLabels, this.yearLabelsSelection, @@ -1393,7 +1488,7 @@ export class Timeline implements powerbiVisualsApi.extensibility.visual.IVisual } } - if (settings.labels.displayAll.value || granularityType === GranularityType.quarter) { + if (settings.labels.displayQuarters.value || granularityType === GranularityType.quarter) { this.renderLabels( extendedLabels.quarterLabels, this.quarterLabelsSelection, @@ -1404,7 +1499,7 @@ export class Timeline implements powerbiVisualsApi.extensibility.visual.IVisual } } - if (settings.labels.displayAll.value || granularityType === GranularityType.month) { + if (settings.labels.displayMonths.value || granularityType === GranularityType.month) { this.renderLabels( extendedLabels.monthLabels, this.monthLabelsSelection, @@ -1415,7 +1510,7 @@ export class Timeline implements powerbiVisualsApi.extensibility.visual.IVisual } } - if (settings.labels.displayAll.value || granularityType === GranularityType.week) { + if (settings.labels.displayWeeks.value || granularityType === GranularityType.week) { this.renderLabels( extendedLabels.weekLabels, this.weekLabelsSelection, @@ -1426,7 +1521,7 @@ export class Timeline implements powerbiVisualsApi.extensibility.visual.IVisual } } - if (settings.labels.displayAll.value || granularityType === GranularityType.day) { + if (settings.labels.displayDays.value || granularityType === GranularityType.day) { this.renderLabels( extendedLabels.dayLabels, this.dayLabelsSelection, @@ -1624,4 +1719,11 @@ export class Timeline implements powerbiVisualsApi.extensibility.visual.IVisual this.isForceSelectionReset = true; } + + private getGranularityType(): GranularityType { + const granularityType: GranularityType = this.visualSettings.granularity.granularity.value + ? this.visualSettings.granularity.granularity.value.value + : GranularityType.month; + return granularityType; + } } diff --git a/src/timeLineSettingsModel.ts b/src/timeLineSettingsModel.ts index a4b8cc7..25755b7 100644 --- a/src/timeLineSettingsModel.ts +++ b/src/timeLineSettingsModel.ts @@ -118,6 +118,13 @@ export class CalendarSettingsCard extends Card { public static readonly DefaultMonth: number = 0; public static readonly DefaultDay: number = 1; + treatAsEndOfFiscalYear = new formattingSettings.ToggleSwitch({ + name: "treatAsEndOfFiscalYear", + displayName: "Treat as end of fiscal year", + displayNameKey: "Visual_TreatAsEndOfFiscalYear", + value: true, + }); + month = new formattingSettings.ItemDropdown({ name: "month", displayName: "Month", @@ -141,7 +148,7 @@ export class CalendarSettingsCard extends Card { displayName: string = "Fiscal Year"; displayNameKey: string = "Visual_FiscalYear"; descriptionKey: string = "Visual_FiscalYear_Description"; - slices = [this.month, this.day]; + slices = [this.treatAsEndOfFiscalYear, this.month, this.day]; } class WeekDaySettingsCard extends Card { @@ -188,10 +195,6 @@ export class RangeHeaderSettingsCard extends Card { displayName: "Text Size", displayNameKey: "Visual_TextSize", value: TextSizeDefaults.Default, - options: { - minValue: { value: TextSizeDefaults.Min, type: ValidatorType.Min }, - maxValue: { value: TextSizeDefaults.Max, type: ValidatorType.Max }, - } }); topLevelSlice = this.show; @@ -387,10 +390,38 @@ export class LabelsSettingsCard extends Card { value: true, }); - displayAll = new formattingSettings.ToggleSwitch({ - name: "displayAll", - displayName: "Display all", - displayNameKey: "Visual_DisplayAll", + displayYears = new formattingSettings.ToggleSwitch({ + name: "displayYears", + displayName: "Display years", + displayNameKey: "Visual_DisplayYears", + value: true, + }); + + displayQuarters = new formattingSettings.ToggleSwitch({ + name: "displayQuarters", + displayName: "Display quarters", + displayNameKey: "Visual_DisplayQuarters", + value: true, + }); + + displayMonths = new formattingSettings.ToggleSwitch({ + name: "displayMonths", + displayName: "Display months", + displayNameKey: "Visual_DisplayMonths", + value: true, + }); + + displayWeeks = new formattingSettings.ToggleSwitch({ + name: "displayWeeks", + displayName: "Display weeks", + displayNameKey: "Visual_DisplayWeeks", + value: true, + }); + + displayDays = new formattingSettings.ToggleSwitch({ + name: "displayDays", + displayName: "Display days", + displayNameKey: "Visual_DisplayDays", value: true, }); @@ -416,7 +447,15 @@ export class LabelsSettingsCard extends Card { name: string = "labels"; displayName: string = "Labels"; displayNameKey: string = "Visual_Labels"; - slices = [this.displayAll, this.fontColor, this.textSize]; + slices = [ + this.displayYears, + this.displayQuarters, + this.displayMonths, + this.displayWeeks, + this.displayDays, + this.fontColor, + this.textSize, + ]; } class ScrollAutoAdjustmentSettingsCard extends Card { diff --git a/stringResources/en-US/resources.resjson b/stringResources/en-US/resources.resjson index 1d4c0e2..1bbe934 100644 --- a/stringResources/en-US/resources.resjson +++ b/stringResources/en-US/resources.resjson @@ -6,6 +6,7 @@ "Visual_FiscalYearStart": "Fiscal Year Start", "Visual_FiscalYear": "Fiscal Year", "Visual_FiscalYear_Description": "This option have no sense if ISO 8601 standard was picked", + "Visual_TreatAsEndOfFiscalYear": "Treat as end of fiscal year", "Visual_Month": "Month", "Visual_Month_January": "January", "Visual_Month_February": "February", @@ -56,6 +57,11 @@ "Visual_FirstDayOfWeek": "First Day of Week", "Visual_FirstDayOfWeek_Description": "This option have no sense if ISO 8601 standard was picked", "Visual_DisplayAll": "Display all", + "Visual_DisplayYears": "Display years", + "Visual_DisplayQuarters": "Display quarters", + "Visual_DisplayMonths": "Display months", + "Visual_DisplayWeeks": "Display weeks", + "Visual_DisplayDays": "Display days", "Visual_Cursor": "Cursor", "Visual_CursorColor": "Cursor color", "Visual_ForceSelection": "Force selection", diff --git a/test/visual.test.ts b/test/visual.test.ts index bb8d0fb..1a565d0 100644 --- a/test/visual.test.ts +++ b/test/visual.test.ts @@ -27,7 +27,7 @@ import {select as d3Select} from "d3-selection"; import powerbiVisualsApi from "powerbi-visuals-api"; import {assertColorsMatch, d3Click, parseColorString, renderTimeout,} from "powerbi-visuals-utils-testutils"; -import {Calendar, CalendarFormat, WeekdayFormat} from "../src/calendars/calendar"; +import {Calendar, CalendarFormat, CalendarFormattingSettings, WeekdayFormat} from "../src/calendars/calendar"; import {ITimelineCursorOverElement, ITimelineData} from "../src/dataInterfaces"; import {ITimelineDatePeriod, ITimelineDatePeriodBase} from "../src/datePeriod/datePeriod"; import {DatePeriodBase} from "../src/datePeriod/datePeriodBase"; @@ -963,7 +963,6 @@ describe("Timeline", () => { beforeEach(() => { dataView.metadata.objects = { labels: { - displayAll: true, show: true, }, }; @@ -980,16 +979,6 @@ describe("Timeline", () => { expect(visualBuilder.allLabels.length).toBe(0); }); - it("shows only selected granularity label if displayAll is set to false", () => { - visualBuilder.updateFlushAllD3Transitions(dataView); - // All labels should be visible - expect(visualBuilder.allLabels.length).toBeGreaterThan(1); - ((dataView.metadata.objects)).labels.displayAll = false; - visualBuilder.updateFlushAllD3Transitions(dataView); - // Only one label should be visible - expect(visualBuilder.allLabels.length).toBe(1); - }); - it("font color", () => { const color: string = "#ABCDEF"; @@ -1029,11 +1018,11 @@ describe("Timeline - Granularity - 1 Jan (Regular Calendar)", () => { calendar = createCalendar(); granularities = [ - new YearGranularity(calendar, null, null), - new QuarterGranularity(calendar, null), - new WeekGranularity(calendar, null, null), - new MonthGranularity(calendar, null), - new DayGranularity(calendar, null), + new YearGranularity(calendar, "en-US", null), + new QuarterGranularity(calendar, "en-US"), + new WeekGranularity(calendar, "en-US", null), + new MonthGranularity(calendar, "en-US"), + new DayGranularity(calendar, "en-US"), ]; }); @@ -1075,11 +1064,11 @@ describe("Timeline - Granularity - 1 Apr (Fiscal Calendar)", () => { calendar = createCalendar(3); granularities = [ - new YearGranularity(calendar, null, null), - new QuarterGranularity(calendar, null), - new WeekGranularity(calendar, null, null), - new MonthGranularity(calendar, null), - new DayGranularity(calendar, null), + new YearGranularity(calendar, "en-US", null), + new QuarterGranularity(calendar, "en-US"), + new WeekGranularity(calendar, "en-US", null), + new MonthGranularity(calendar, "en-US"), + new DayGranularity(calendar, "en-US"), ]; }); @@ -1157,7 +1146,8 @@ describe("Timeline - Granularity - ISO 8601 Week numbering", () => { let calendar: Calendar; beforeEach(() => { - calendar = new CalendarISO8061(); + const calendarFormattingSettings: CalendarFormattingSettings = { treatAsEndOfFiscalYear: true }; + calendar = new CalendarISO8061(calendarFormattingSettings); }); describe("ISO Calendar Methods", () => { @@ -1723,6 +1713,7 @@ function createCalendar( day: number = 1, week: number = 1, dayOfWeekSelectionOn: boolean = false, + treatAsEndOfFiscalYear: boolean = true, ): Calendar { const calendarSettings: CalendarFormat = { @@ -1735,7 +1726,7 @@ function createCalendar( daySelection: dayOfWeekSelectionOn, }; - return new Calendar(calendarSettings, weekDaySettings); + return new Calendar(calendarSettings, weekDaySettings, { treatAsEndOfFiscalYear }); } function createDatePeriod(dates: Date[]): ITimelineDatePeriod[] {