diff --git a/THIRD-PARTY-LICENSES.csv b/THIRD-PARTY-LICENSES.csv index 998c0450a5..49a344d4ec 100644 --- a/THIRD-PARTY-LICENSES.csv +++ b/THIRD-PARTY-LICENSES.csv @@ -853,6 +853,7 @@ "neo-async@2.6.2","MIT","https://github.com/suguru03/neo-async" "ng2-ui-auth@10.0.1","UNKNOWN","" "ngx-markdown@14.0.1","MIT","https://github.com/jfcere/ngx-markdown" +"ngx-mat-select-search@5.0.0","MIT","https://github.com/bithost-gmbh/ngx-mat-select-search" "ngx-sharebuttons@8.1.0","MIT","https://github.com/murhafsousli/ngx-sharebuttons" "nice-napi@1.0.2","MIT","https://github.com/addaleax/nice-napi" "node-addon-api@3.2.1","MIT","https://github.com/nodejs/node-addon-api" diff --git a/cypress/e2e/group2/myworkflows.ts b/cypress/e2e/group2/myworkflows.ts index bb6cc643a8..8c5d186596 100644 --- a/cypress/e2e/group2/myworkflows.ts +++ b/cypress/e2e/group2/myworkflows.ts @@ -521,6 +521,18 @@ describe('Dockstore my workflows', () => { }); } }); +describe('Version Dropdown should have search capabilities', () => { + setTokenUserViewPort(); + it('Search result should exclude master version', () => { + cy.visit('/my-workflows/github.com/A/l'); + cy.get('[data-cy=version-dropdown]').should('contain', 'master').click(); + cy.get('mat-option').should('contain', 'master').should('be.visible'); + cy.get('mat-option').should('contain', 'test').should('be.visible'); + cy.get('[data-cy=version-dropdown-search-field]').should('be.visible').type('test'); + cy.get('mat-option').should('not.contain', 'master'); + cy.get('mat-option').should('contain', 'test').should('be.visible'); + }); +}); describe('Should handle no workflows correctly', () => { resetDB(); setTokenUserViewPortCurator(); // Curator has no workflows diff --git a/package-lock.json b/package-lock.json index 8816f13a53..80a3a3349f 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "dockstore-ui2", - "version": "2.8.0", + "version": "2.10.0", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "dockstore-ui2", - "version": "2.8.0", + "version": "2.10.0", "hasInstallScript": true, "license": "Apache License 2.0", "dependencies": { @@ -41,6 +41,7 @@ "material-design-icons-iconfont": "^6.1.0", "ng2-ui-auth": "^10.0.1", "ngx-markdown": "^14.0.1", + "ngx-mat-select-search": "^5.0.0", "ngx-sharebuttons": "^8.0.5", "rxjs": "^6.6.7", "ts-md5": "^1.2.8", @@ -12671,6 +12672,17 @@ "zone.js": "^0.11.4" } }, + "node_modules/ngx-mat-select-search": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/ngx-mat-select-search/-/ngx-mat-select-search-5.0.0.tgz", + "integrity": "sha512-w9NLIhN/b6aDpvwdrvxttNm6eYbcnalyCIOPaFPTpK8Ub8graHmL6qQdWzIw54Qxyzlr7A0DvpMgywcs4eGSYA==", + "dependencies": { + "tslib": "^2.4.0" + }, + "peerDependencies": { + "@angular/material": "^12.0.0 || ^13.0.0 || ^14.0.0" + } + }, "node_modules/ngx-sharebuttons": { "version": "8.1.0", "resolved": "https://registry.npmjs.org/ngx-sharebuttons/-/ngx-sharebuttons-8.1.0.tgz", @@ -26965,6 +26977,14 @@ "tslib": "^2.3.0" } }, + "ngx-mat-select-search": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/ngx-mat-select-search/-/ngx-mat-select-search-5.0.0.tgz", + "integrity": "sha512-w9NLIhN/b6aDpvwdrvxttNm6eYbcnalyCIOPaFPTpK8Ub8graHmL6qQdWzIw54Qxyzlr7A0DvpMgywcs4eGSYA==", + "requires": { + "tslib": "^2.4.0" + } + }, "ngx-sharebuttons": { "version": "8.1.0", "resolved": "https://registry.npmjs.org/ngx-sharebuttons/-/ngx-sharebuttons-8.1.0.tgz", diff --git a/package.json b/package.json index 1066de933e..7b865f3046 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "dockstore-ui2", - "version": "2.8.0", + "version": "2.10.0", "license": "Apache License 2.0", "config": { "webservice_version": "1.13.0", @@ -74,6 +74,7 @@ "material-design-icons-iconfont": "^6.1.0", "ng2-ui-auth": "^10.0.1", "ngx-markdown": "^14.0.1", + "ngx-mat-select-search": "^5.0.0", "ngx-sharebuttons": "^8.0.5", "rxjs": "^6.6.7", "ts-md5": "^1.2.8", diff --git a/src/app/app.module.ts b/src/app/app.module.ts index 64a7df60a9..fba41e0daf 100644 --- a/src/app/app.module.ts +++ b/src/app/app.module.ts @@ -113,6 +113,7 @@ import { TosBannerComponent } from './tosBanner/tos-banner.component'; import { ExporterStepComponent } from './workflow/snapshot-exporter-modal/exporter-step/exporter-step.component'; import { SnaphotExporterModalComponent } from './workflow/snapshot-exporter-modal/snaphot-exporter-modal.component'; import { ViewService } from './workflow/view/view.service'; +import { NgxMatSelectSearchModule } from 'ngx-mat-select-search'; export const myCustomTooltipDefaults: MatTooltipDefaultOptions = { showDelay: 500, @@ -183,6 +184,7 @@ export function configurationServiceFactory(configurationService: ConfigurationS StarringModule, OrganizationStarringModule, OrganizationStargazersModule, + NgxMatSelectSearchModule, routing, StargazersModule, MarkdownModule.forRoot(), diff --git a/src/app/shared/modules/workflow.module.ts b/src/app/shared/modules/workflow.module.ts index 1366f059bb..e58899d849 100644 --- a/src/app/shared/modules/workflow.module.ts +++ b/src/app/shared/modules/workflow.module.ts @@ -60,6 +60,7 @@ import { SnackbarModule } from './snackbar.module'; import { CategoryButtonModule } from './../../categories/button/category-button.module'; import { MySidebarModule } from '../modules/my-sidebar.module'; import { SourceFileTabsService } from '../../source-file-tabs/source-file-tabs.service'; +import { NgxMatSelectSearchModule } from 'ngx-mat-select-search'; @NgModule({ declarations: [ @@ -100,6 +101,7 @@ import { SourceFileTabsService } from '../../source-file-tabs/source-file-tabs.s SnackbarModule, CategoryButtonModule, MySidebarModule, + NgxMatSelectSearchModule, ], providers: [ DateService, diff --git a/src/app/workflow/workflow.component.html b/src/app/workflow/workflow.component.html index 5a0faa3655..2afd369918 100644 --- a/src/app/workflow/workflow.component.html +++ b/src/app/workflow/workflow.component.html @@ -71,10 +71,22 @@

0" class="input-group"> - - - {{ workflowVersion.name }} + + + + + + + {{ version.name }} diff --git a/src/app/workflow/workflow.component.ts b/src/app/workflow/workflow.component.ts index 929fbfb2f2..74c1aa4fc9 100644 --- a/src/app/workflow/workflow.component.ts +++ b/src/app/workflow/workflow.component.ts @@ -22,7 +22,7 @@ import { ActivatedRoute, Router } from '@angular/router'; import { AlertService } from 'app/shared/alert/state/alert.service'; import { BioWorkflow } from 'app/shared/swagger/model/bioWorkflow'; import { Service } from 'app/shared/swagger/model/service'; -import { Observable } from 'rxjs'; +import { Observable, ReplaySubject } from 'rxjs'; import { finalize, takeUntil } from 'rxjs/operators'; import { AlertQuery } from '../shared/alert/state/alert.query'; import { BioschemaService } from '../shared/bioschema.service'; @@ -58,6 +58,7 @@ import { EntriesService, WorkflowSubClass } from '../shared/openapi'; import { Title } from '@angular/platform-browser'; import { EntryCategoriesService } from '../categories/state/entry-categories.service'; import RoleEnum = Permission.RoleEnum; +import { FormControl } from '@angular/forms'; @Component({ selector: 'app-workflow', @@ -97,6 +98,11 @@ export class WorkflowComponent extends Entry implements AfterViewInit, OnInit { public WorkflowModel = Workflow; public WorkflowVersionModel = WorkflowVersion; public launchSupport$: Observable; + public workflowVersionAlphabetical: Array = []; + public workflowVersionsCtrl: FormControl = new FormControl(null); //control for the selected version + public versionFilterCtrl: FormControl = new FormControl(''); // control for the MatSelect filter keyword + public filteredVersions: ReplaySubject> = new ReplaySubject>(1); + @Input() user; constructor( @@ -167,6 +173,10 @@ export class WorkflowComponent extends Entry implements AfterViewInit, OnInit { ngOnInit() { this.init(); + //watch for changes in search + this.versionFilterCtrl.valueChanges.pipe(takeUntil(this.ngUnsubscribe)).subscribe(() => { + this.filterVersions(); + }); } ngAfterViewInit() { @@ -181,7 +191,6 @@ export class WorkflowComponent extends Entry implements AfterViewInit, OnInit { } }); } - this.updateTabSelection(); } @@ -302,11 +311,31 @@ export class WorkflowComponent extends Entry implements AfterViewInit, OnInit { this.gA4GHFilesService.updateFiles(trsID, this.selectedVersion.name, [this.workflow.descriptorType]); } } + this.workflowVersionAlphabetical = this.workflow.workflowVersions.slice().sort((a, b) => { + return a.name.localeCompare(b.name); + }); + this.filteredVersions.next(this.workflowVersionAlphabetical.slice()); + this.workflowVersionsCtrl.setValue(this.selectedVersion); } this.setUpWorkflow(workflow); }); } + //function for filtering list of versions according to search + protected filterVersions() { + if (!this.workflowVersionAlphabetical) { + return; + } + let search = this.versionFilterCtrl.value; + if (!search) { + this.filteredVersions.next(this.workflowVersionAlphabetical.slice()); + return; + } else { + search = search.toLowerCase(); + } + this.filteredVersions.next(this.workflowVersionAlphabetical.filter((version) => version.name.toLowerCase().indexOf(search) > -1)); + } + public getTRSID(): string { let id: string; if (this.entryType === EntryType.Tool) {