diff --git a/package-lock.json b/package-lock.json index 0d0afae..e19501d 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9,10 +9,9 @@ "version": "1.5.3", "license": "Apache-2.0", "dependencies": { - "@types/chosen-js": "^1.8.6", - "chosen-js": "^1.8.7", "jquery": "^4.0.0", "jquery-ui-dist": "^1.13.3", + "tom-select": "^2.6.0", "vss-web-extension-sdk": "^5.141.0" }, "devDependencies": { @@ -87,6 +86,21 @@ "node": ">= 8" } }, + "node_modules/@orchidjs/sifter": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@orchidjs/sifter/-/sifter-1.1.0.tgz", + "integrity": "sha512-mYwHCfr736cIWWdhhSZvDbf90AKt2xyrJspKFC3qyIJG1LtrJeJunYEqCGG4Aq2ijENbc4WkOjszcvNaIAS/pQ==", + "license": "Apache-2.0", + "dependencies": { + "@orchidjs/unicode-variants": "^1.1.2" + } + }, + "node_modules/@orchidjs/unicode-variants": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@orchidjs/unicode-variants/-/unicode-variants-1.1.2.tgz", + "integrity": "sha512-5DobW1CHgnBROOEpFlEXytED5OosEWESFvg/VYmH0143oXcijYTprRYJTs+55HzGM4IqxiLFSuqEzu9mPNwVsA==", + "license": "Apache-2.0" + }, "node_modules/@pkgjs/parseargs": { "version": "0.11.0", "resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz", @@ -98,15 +112,6 @@ "node": ">=14" } }, - "node_modules/@types/chosen-js": { - "version": "1.8.6", - "resolved": "https://registry.npmjs.org/@types/chosen-js/-/chosen-js-1.8.6.tgz", - "integrity": "sha512-ql+NU3FvB7o4CbUwbDKwhTRsZ+9UATyuUl4DjYudT5q9JNlrGtHg9vyAGLjRs1BraoNhenTQTTIn1382CgssQA==", - "license": "MIT", - "dependencies": { - "@types/jquery": "*" - } - }, "node_modules/@types/jquery": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/@types/jquery/-/jquery-4.0.0.tgz", @@ -634,12 +639,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/chosen-js": { - "version": "1.8.7", - "resolved": "https://registry.npmjs.org/chosen-js/-/chosen-js-1.8.7.tgz", - "integrity": "sha512-eVdrZJ2U5ISdObkgsi0od5vIJdLwq1P1Xa/Vj/mgxkMZf14DlgobfB6nrlFi3kW4kkvKLsKk4NDqZj1MU1DCpw==", - "license": "MIT" - }, "node_modules/clipboardy": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/clipboardy/-/clipboardy-4.0.0.tgz", @@ -3558,6 +3557,23 @@ "node": ">=8.0" } }, + "node_modules/tom-select": { + "version": "2.6.0", + "resolved": "https://registry.npmjs.org/tom-select/-/tom-select-2.6.0.tgz", + "integrity": "sha512-o2ToBjhUAnrrQvW/hrY9c//TpOpAKYSlfuFnf0DIwNy+ua+mmYnsF4PxN/PpzBfUIfEFkNYAngeGBfOAZWF3tw==", + "license": "Apache-2.0", + "dependencies": { + "@orchidjs/sifter": "^1.1.0", + "@orchidjs/unicode-variants": "^1.1.2" + }, + "engines": { + "node": "*" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/tom-select" + } + }, "node_modules/tracer": { "version": "0.7.4", "resolved": "https://registry.npmjs.org/tracer/-/tracer-0.7.4.tgz", diff --git a/package.json b/package.json index a20b5af..cf62859 100644 --- a/package.json +++ b/package.json @@ -26,10 +26,9 @@ "typescript": "^5.9.3" }, "dependencies": { - "@types/chosen-js": "^1.8.6", - "chosen-js": "^1.8.7", "jquery": "^4.0.0", "jquery-ui-dist": "^1.13.3", + "tom-select": "^2.6.0", "vss-web-extension-sdk": "^5.141.0" }, "overrides": { diff --git a/sites/widget-configuration.html b/sites/widget-configuration.html index fb9f8a2..d0884db 100644 --- a/sites/widget-configuration.html +++ b/sites/widget-configuration.html @@ -4,10 +4,10 @@ - + - +
diff --git a/src/DashboardWidgetExtension/WidgetConfiguration.ts b/src/DashboardWidgetExtension/WidgetConfiguration.ts index 65c667f..990a87a 100644 --- a/src/DashboardWidgetExtension/WidgetConfiguration.ts +++ b/src/DashboardWidgetExtension/WidgetConfiguration.ts @@ -14,6 +14,8 @@ import NotificationUtils from '../Utils/NotificationUtils'; import {ExtensionSetting} from "../Settings/ExtensionSetting"; import UiUtils = require('../Utils/UiUtils'); +declare var TomSelect: any; + export class Configuration { private projectSettings: ProjectSettings = null; private organizationSettings: Settings = null; @@ -34,6 +36,10 @@ export class Configuration { private testGapCheckbox = $('#show-test-gap'); private separateTgaServerCheckbox = $('#separate-tga-server'); + private tsProjectSelect: any; + private tsTgaProjectSelect: any; + private tsBaselineSelect: any; + private widgetHelpers: any; private readonly notificationService: any; private readonly controlService: any; @@ -63,10 +69,15 @@ export class Configuration { this.separateTgaServerCheckbox.prop('checked', this.widgetSettings.useSeparateTgaServer); } this.zipTgaConfiguration(); - $('#teamscale-project-select').chosen({width: '100%'}).on('change', - () => this.fillDropdownWithTeamscaleBaselines(notifyWidgetChange)); - $('#teamscale-tga-project-select').chosen({width: '100%'}).on('change', notifyWidgetChange); - $('#ts-baseline-select').chosen({width: '95%'}).on('change', notifyWidgetChange); + + this.tsProjectSelect = new TomSelect('#teamscale-project-select', {}); + this.tsProjectSelect.on('change', () => this.fillDropdownWithTeamscaleBaselines(notifyWidgetChange)); + + this.tsTgaProjectSelect = new TomSelect('#teamscale-tga-project-select', {}); + this.tsTgaProjectSelect.on('change', notifyWidgetChange); + + this.tsBaselineSelect = new TomSelect('#ts-baseline-select', {}); + this.tsBaselineSelect.on('change', notifyWidgetChange); this.loadAndCheckConfiguration().then(() => this.fillDropdownsWithProjects()) .then(() => this.fillDropdownWithTeamscaleBaselines(notifyWidgetChange)) @@ -129,12 +140,8 @@ export class Configuration { } private handleMissingTgaServerConfig() { - const element = document.createElement('option'); - element.textContent = 'Error: No TGA server configured. Deactivate separate Server option or' + - ' configure TGA Server.'; - this.teamscaleTgaProjectSelect.appendChild(element); - $('#' + this.teamscaleTgaProjectSelect.id).prop('disabled', true); - $('#' + this.teamscaleTgaProjectSelect.id).trigger('chosen:updated'); + this.tsTgaProjectSelect.addOption({value: '', text: 'Error: No TGA server configured. Deactivate separate Server option or configure TGA Server.'}); + this.tsTgaProjectSelect.disable(); return Promise.resolve(); } @@ -149,7 +156,7 @@ export class Configuration { * Loads a list of accessible projects from the Teamscale server and appends them to the TQE dropdown menu. */ private async fillTqeDropdownWithProjects() { - return this.fillDropdownWithProjects(this.teamscaleClient, this.teamscaleProjectSelect, '#teamscale-project-select'); + return this.fillDropdownWithProjects(this.teamscaleClient, this.tsProjectSelect, 'teamscaleProject'); } /** @@ -164,13 +171,13 @@ export class Configuration { } this.tgaTeamscaleClient = new TeamscaleClient(tgaUrl); } - return this.fillDropdownWithProjects(this.tgaTeamscaleClient, this.teamscaleTgaProjectSelect, '#teamscale-tga-project-select'); + return this.fillDropdownWithProjects(this.tgaTeamscaleClient, this.tsTgaProjectSelect, 'tgaTeamscaleProject'); } /** * Loads a list of accessible projects from the Teamscale server and appends them to the dropdown menu. */ - private async fillDropdownWithProjects(teamscaleClient: TeamscaleClient, selectElement: HTMLSelectElement, selectElementId: string) { + private async fillDropdownWithProjects(teamscaleClient: TeamscaleClient, tomSelect: any, settingsKey: string) { let projects: string[]; try { projects = await teamscaleClient.retrieveTeamscaleProjects(); @@ -179,17 +186,14 @@ export class Configuration { return Promise.reject(error); } + tomSelect.clearOptions(); for (const project of projects) { - const element = document.createElement('option'); - element.textContent = project; - element.value = project; - if (this.widgetSettings) { - element.selected = this.widgetSettings.teamscaleProject === project; - } - selectElement.appendChild(element); + tomSelect.addOption({value: project, text: project}); } - $(selectElementId).trigger('chosen:updated'); + if (this.widgetSettings && this.widgetSettings[settingsKey]) { + tomSelect.setValue(this.widgetSettings[settingsKey], true); + } } /** @@ -197,7 +201,7 @@ export class Configuration { */ private async fillDropdownWithTeamscaleBaselines(notifyWidgetChange) { // use input value and not widgetSetting Object which might hold an outdated project name - // since the chosen change event of the project selector is fired before the settings object update + // since the change event of the project selector is fired before the settings object update const teamscaleProject: string = this.teamscaleProjectSelect.value; let baselines: ITeamscaleBaseline[]; @@ -209,26 +213,22 @@ export class Configuration { return Promise.reject(error); } - while (this.teamscaleBaselineSelect.firstChild) { - this.teamscaleBaselineSelect.removeChild(this.teamscaleBaselineSelect.firstChild); - } + this.tsBaselineSelect.clearOptions(); this.disableBaselineDropdownForProjectsWithoutBaselines(baselines, teamscaleProject); for (const baseline of baselines) { - const element = document.createElement('option'); const date = new Date(baseline.timestamp); - element.textContent = baseline.name + ' (' + date.toLocaleDateString() + ')'; - element.value = baseline.name; - if (this.widgetSettings) { - element.selected = this.widgetSettings.tsBaseline === baseline.name; - } - this.teamscaleBaselineSelect.appendChild(element); + const text = baseline.name + ' (' + date.toLocaleDateString() + ')'; + this.tsBaselineSelect.addOption({value: baseline.name, text: text}); + } + + if (this.widgetSettings && this.widgetSettings.tsBaseline) { + this.tsBaselineSelect.setValue(this.widgetSettings.tsBaseline, true); } - // update widget settings to get rid of a baseline which belongs to the formally chosen project + // update widget settings to get rid of a baseline which belongs to the formerly chosen project this.getAndUpdateCustomSettings(); - $('#ts-baseline-select').trigger('chosen:updated'); notifyWidgetChange(); } @@ -237,12 +237,10 @@ export class Configuration { */ private disableBaselineDropdownForProjectsWithoutBaselines(baselines: ITeamscaleBaseline[], teamscaleProject: string) { if (baselines.length === 0) { - const element = document.createElement('option'); - element.textContent = 'No baseline configured for project »' + teamscaleProject + '«'; - this.teamscaleBaselineSelect.appendChild(element); - $('#ts-baseline-select').prop('disabled', true); + this.tsBaselineSelect.addOption({value: '', text: 'No baseline configured for project »' + teamscaleProject + '«'}); + this.tsBaselineSelect.disable(); } else { - $('#ts-baseline-select').prop('disabled', false); + this.tsBaselineSelect.enable(); } } diff --git a/tsconfig.json b/tsconfig.json index 1a07241..3b91fdd 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -9,8 +9,7 @@ "rootDir": "src/", "outDir": "dist/", "types": [ - "vss-web-extension-sdk", - "chosen-js" + "vss-web-extension-sdk" ], "lib": [ "es2015", "dom" ] } diff --git a/vss-extension.json b/vss-extension.json index c4eb028..dfbe9a0 100644 --- a/vss-extension.json +++ b/vss-extension.json @@ -168,14 +168,14 @@ "packagePath": "lib/VSS.SDK.min.js" }, { - "path": "node_modules/chosen-js/chosen.jquery.min.js", + "path": "node_modules/tom-select/dist/js/tom-select.complete.min.js", "addressable": true, - "packagePath": "lib/chosen.jquery.min.js" + "packagePath": "lib/tom-select.complete.min.js" }, { - "path": "node_modules/chosen-js/chosen.min.css", + "path": "node_modules/tom-select/dist/css/tom-select.default.min.css", "addressable": true, - "packagePath": "styles/chosen.min.css" + "packagePath": "styles/tom-select.default.min.css" }, { "path": "node_modules/jquery/dist/jquery.min.js", @@ -197,11 +197,6 @@ "addressable": true, "packagePath": "styles/images/ui-icons_444444_256x240.png" }, - { - "path": "node_modules/chosen-js/chosen-sprite.png", - "addressable": true, - "packagePath": "styles/chosen-sprite.png" - }, { "path": "scripts/require.js", "addressable": true