Skip to content

Commit

Permalink
Feature/2325/tool table (#620)
Browse files Browse the repository at this point in the history
* Convert to material table, allow horizontal scroll

* Add some tests

* Add loading bar

* Move booleans into component file

* Refactor

* Remove unneeded style
  • Loading branch information
garyluu authored Apr 9, 2019
1 parent 5f194a2 commit 001c1ed
Show file tree
Hide file tree
Showing 7 changed files with 196 additions and 95 deletions.
103 changes: 32 additions & 71 deletions src/app/workflow/tool-tab/tool-tab.component.html
Original file line number Diff line number Diff line change
Expand Up @@ -14,76 +14,37 @@
~ limitations under the License.
-->
<div class="p-3">
<div class="row">
<div class="col-md-12">
<table class="table versions-grid table-bordered table-condensed table-striped">
<thead>
<tr>
<th>
Workflow Excerpt
</th>
<th *ngIf="(descriptorType$ | async) === ToolDescriptor.TypeEnum.CWL">
Tool Excerpt
</th>
<th *ngIf="(descriptorType$ | async) === ToolDescriptor.TypeEnum.WDL">
Task Excerpt
</th>
<th *ngIf="(descriptorType$ | async) === ToolDescriptor.TypeEnum.NFL">
Process Excerpt
</th>
</tr>
</thead>
<tbody>
<tr *ngIf="!toolsContent">
<td colspan="10">
<div class="text-center">No Records Found</div>
</td>
</tr>
<tr *ngIf="toolsContent && toolsContent.length === 0">
<td colspan="10">
<div class="text-center">Some tools associated with this workflow are missing from Git repo.</div>
</td>
</tr>
<tr *ngFor="let tool of toolsContent">
<td *ngIf="(descriptorType$ | async) === ToolDescriptor.TypeEnum.CWL">
<strong>tool ID</strong>: {{tool?.id}}
</td>
<td *ngIf="(descriptorType$ | async) === ToolDescriptor.TypeEnum.CWL">
<strong>run</strong>: {{tool?.file}}
<app-loading [loading]="loading">
<mat-card *ngIf="nullContent" class="alert alert-warning" role="alert">
<mat-icon>warning</mat-icon>
No Records Found
</mat-card>
<mat-card *ngIf="noContent" class="alert alert-warning" role="alert">
<mat-icon>warning</mat-icon>
Some tools associated with this workflow are missing from Git repo.
</mat-card>
<table *ngIf="hasContent" mat-table [dataSource]="toolContent" class="mat-elevation-z1 w-100">
<ng-container matColumnDef="workflowExcerpt">
<th mat-header-cell *matHeaderCellDef> Workflow Excerpt </th>
<td mat-cell *matCellDef="let tool"> <strong>{{workflowExcerptRowHeading$ | async}}</strong>:&nbsp;{{tool?.id}}
</td>
</ng-container>
<ng-container matColumnDef="toolExcerpt">
<th mat-header-cell *matHeaderCellDef> {{ toolExcerptHeaderName$ | async }} </th>
<td mat-cell *matCellDef="let tool">
<div *ngIf="(descriptorType$ | async) === ToolDescriptor.TypeEnum.CWL">
<strong>run</strong>:&nbsp;{{tool?.file}}
<br>
<strong>docker</strong>: {{tool?.docker}}
<br>
<strong>link</strong>:
<a href="{{tool?.link}}" target="_blank" *ngIf="tool?.link !== 'Not Specified'">{{tool?.link}}</a>
<span *ngIf="tool?.link === 'Not Specified'">Not Specified</span>
</td>

<td *ngIf="(descriptorType$ | async) === ToolDescriptor.TypeEnum.WDL">
<strong>task ID</strong>: {{tool?.id}}
<br>
</td>
<td *ngIf="(descriptorType$ | async) === ToolDescriptor.TypeEnum.WDL">
<strong>docker</strong>: {{tool?.docker}}
<br>
<strong>link</strong>:
<a href="{{tool?.link}}" target="_blank" *ngIf="tool?.link !== 'Not Specified'">{{tool?.link}}</a>
<span *ngIf="tool?.link === 'Not Specified'">Not Specified</span>
</td>

<td *ngIf="(descriptorType$ | async) === ToolDescriptor.TypeEnum.NFL">
<strong>process name</strong>: {{tool?.id}}
<br>
</td>
<td *ngIf="(descriptorType$ | async) === ToolDescriptor.TypeEnum.NFL">
<strong>docker</strong>: {{tool?.docker}}
<br>
<strong>link</strong>:
<a href="{{tool?.link}}" target="_blank" *ngIf="tool?.link !== 'Not Specified'">{{tool?.link}}</a>
<span *ngIf="tool?.link === 'Not Specified'">Not Specified</span>
</td>
</tr>
</tbody>
</table>
</div>
</div>
</div>
<strong>docker</strong>:&nbsp;{{tool?.docker}}
<br>
<strong>link</strong>:&nbsp;<a href="{{tool?.link}}" target="_blank"
*ngIf="tool?.link !== 'Not Specified'">{{tool?.link}}</a>
<span *ngIf="tool?.link === 'Not Specified'">Not Specified</span>
</td>
</ng-container>
<tr mat-header-row *matHeaderRowDef="displayedColumns"></tr>
<tr mat-row *matRowDef="let row; columns: displayedColumns;"></tr>
</table>
</app-loading>
</div>
Original file line number Diff line number Diff line change
Expand Up @@ -13,4 +13,3 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/

25 changes: 13 additions & 12 deletions src/app/workflow/tool-tab/tool-tab.component.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,8 @@
*/
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
import { FormsModule } from '@angular/forms';

import { RefreshAlertModule } from '../../shared/alert/alert.module';
import { CustomMaterialModule } from '../../shared/modules/material.module';
import { WorkflowService } from '../../shared/state/workflow.service';
import { WorkflowsService } from '../../shared/swagger';
import { WorkflowsStubService, WorkflowStubService } from '../../test/service-stubs';
Expand All @@ -27,13 +28,13 @@ describe('ToolTabComponent', () => {

beforeEach(async(() => {
TestBed.configureTestingModule({
declarations: [ ToolTabComponent ],
imports: [FormsModule],
declarations: [ToolTabComponent],
imports: [FormsModule, CustomMaterialModule, RefreshAlertModule],
providers: [
{provide: WorkflowService, useClass: WorkflowStubService},
{provide: WorkflowsService, useClass: WorkflowsStubService}]
{ provide: WorkflowService, useClass: WorkflowStubService },
{ provide: WorkflowsService, useClass: WorkflowsStubService }]
})
.compileComponents();
.compileComponents();
}));

beforeEach(() => {
Expand All @@ -47,11 +48,11 @@ describe('ToolTabComponent', () => {
});

it('should update table tool content', () => {
component.updateTableToolContent(1, null);
expect(component.toolsContent).toBe(null);
component.updateTableToolContent(null, 1);
expect(component.toolsContent).toBe(null);
component.updateTableToolContent(1, 1);
expect(component.toolsContent).toBe('tableToolContentString');
component.getTableToolContent(1, null);
expect(component.toolContent).toBe(null);
component.getTableToolContent(null, 1);
expect(component.toolContent).toBe(null);
component.getTableToolContent(1, 1);
expect(component.toolContent).toBe('tableToolContentString');
});
});
77 changes: 67 additions & 10 deletions src/app/workflow/tool-tab/tool-tab.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,39 +15,55 @@
*/
import { Component, Input } from '@angular/core';
import { Observable } from 'rxjs';
import { finalize, map } from 'rxjs/operators';
import { EntryTab } from '../../shared/entry/entry-tab';
import { WorkflowQuery } from '../../shared/state/workflow.query';
import { ToolDescriptor, WorkflowVersion } from '../../shared/swagger';
import { WorkflowsService } from './../../shared/swagger/api/workflows.service';
import { Workflow } from './../../shared/swagger/model/workflow';
import { ToolTabService } from './tool-tab.service';

@Component({
selector: 'app-tool-tab',
templateUrl: './tool-tab.component.html',
styleUrls: ['./tool-tab.component.css']
styleUrls: ['./tool-tab.component.scss']
})
export class ToolTabComponent extends EntryTab {
workflow: Workflow;
toolsContent: string = null;
toolContent: string = null;
_selectedVersion: WorkflowVersion;
descriptorType$: Observable<ToolDescriptor.TypeEnum>;
ToolDescriptor = ToolDescriptor;
toolExcerptHeaderName$: Observable<string>;
workflowExcerptRowHeading$: Observable<string>;
displayedColumns: string[] = ['workflowExcerpt', 'toolExcerpt'];
// TODO: Put most of this stuff in Akita state
hasContent = false;
nullContent = false;
noContent = false;
loading = true;
@Input() set selectedVersion(value: WorkflowVersion) {
if (value != null) {
this.workflow = this.workflowQuery.getActive();
// Also check that the workflow version belongs to the workflow
if (this.workflow && this.workflow.workflowVersions && this.workflow.workflowVersions.some(version => version.id === value.id)) {
this.updateTableToolContent(this.workflow.id, value.id);
this.getTableToolContent(this.workflow.id, value.id);
} else {
this.handleNullToolContent();
console.error('Should not be able to select version without a workflow');
}
} else {
this.toolsContent = null;
this.handleNullToolContent();
}
}
constructor(private workflowQuery: WorkflowQuery, private workflowsService: WorkflowsService) {

constructor(private workflowQuery: WorkflowQuery, private workflowsService: WorkflowsService, private toolTabService: ToolTabService) {
super();
this.descriptorType$ = this.workflowQuery.descriptorType$;
this.toolExcerptHeaderName$ = this.descriptorType$.pipe(
map(descriptorType => this.toolTabService.descriptorTypeToHeaderName(descriptorType)));
this.workflowExcerptRowHeading$ = this.descriptorType$.pipe(
map(descriptorType => this.toolTabService.descriptorTypeToWorkflowExcerptRowHeading(descriptorType)));
}

/**
Expand All @@ -57,17 +73,58 @@ export class ToolTabComponent extends EntryTab {
* @param {number} versionId The workflowVersion Id
* @memberof ToolTabComponent
*/
updateTableToolContent(workflowId: number, versionId: number): void {
getTableToolContent(workflowId: number, versionId: number): void {
if (workflowId && versionId) {
this.workflowsService.getTableToolContent(workflowId, versionId).subscribe(
this.loading = true;
this.workflowsService.getTableToolContent(workflowId, versionId).pipe(finalize(() => this.loading = false)).subscribe(
(toolContent) => {
this.toolsContent = toolContent;
this.handleToolContent(toolContent);
}, error => {
console.log('Could not retrieve table tool content');
this.toolsContent = null;
this.handleToolContent(null);
});
} else {
this.toolsContent = null;
this.handleNullToolContent();
}
}

/**
* Updates the 3 boolean variables that determines what to show (one of the 2 warnings or the table)
*
* @private
* @param {string} toolContent The current workflow version's toolContent
* @memberof ToolTabComponent
*/
private handleToolContent(toolContent: string): void {
this.hasContent = false;
this.noContent = false;
this.nullContent = false;
this.toolContent = toolContent;
if (!toolContent) {
this.hasContent = false;
this.noContent = false;
this.nullContent = true;
} else {
if (toolContent.length === 0) {
this.hasContent = false;
this.noContent = true;
this.nullContent = false;
} else {
this.hasContent = true;
this.noContent = false;
this.nullContent = false;
}
}
}

/**
* Handle when no tool content was able to be retrieved
*
* @private
* @memberof ToolTabComponent
*/
private handleNullToolContent(): void {
this.handleToolContent(null);
this.loading = false;
}
}
29 changes: 29 additions & 0 deletions src/app/workflow/tool-tab/tool-tab.service.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import { inject, TestBed } from '@angular/core/testing';
import { ToolDescriptor } from '../../shared/swagger';
import { ToolTabService } from './tool-tab.service';

describe('Service: ToolTab', () => {
beforeEach(() => {
TestBed.configureTestingModule({
providers: [ToolTabService]
});
});

it('should ...', inject([ToolTabService], (service: ToolTabService) => {
expect(service).toBeTruthy();
}));

it('should determines the correct workflow excerpt row heading', inject([ToolTabService], (service: ToolTabService) => {
expect(service.descriptorTypeToWorkflowExcerptRowHeading(ToolDescriptor.TypeEnum.CWL)).toEqual('tool\xa0ID');
expect(service.descriptorTypeToWorkflowExcerptRowHeading(ToolDescriptor.TypeEnum.WDL)).toEqual('task\xa0ID');
expect(service.descriptorTypeToWorkflowExcerptRowHeading(ToolDescriptor.TypeEnum.NFL)).toEqual('process\xa0name');
expect(service.descriptorTypeToWorkflowExcerptRowHeading(<ToolDescriptor.TypeEnum>'Potato')).toEqual('tool\xa0ID');
}));

it('should determines the correct workflow excerpt row heading', inject([ToolTabService], (service: ToolTabService) => {
expect(service.descriptorTypeToHeaderName(ToolDescriptor.TypeEnum.CWL)).toEqual('Tool Excerpt');
expect(service.descriptorTypeToHeaderName(ToolDescriptor.TypeEnum.WDL)).toEqual('Task Excerpt');
expect(service.descriptorTypeToHeaderName(ToolDescriptor.TypeEnum.NFL)).toEqual('Process Excerpt');
expect(service.descriptorTypeToHeaderName(<ToolDescriptor.TypeEnum>'Potato')).toEqual('Tool Excerpt');
}));
});
54 changes: 54 additions & 0 deletions src/app/workflow/tool-tab/tool-tab.service.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
import { Injectable } from '@angular/core';
import { ToolDescriptor } from '../../shared/swagger';

@Injectable({
providedIn: 'root'
})
export class ToolTabService {

constructor() { }

/**
* Determines the second column's header name. No break needed because headings are always short.
*
* @param {ToolDescriptor.TypeEnum} descriptorType The current workflow's descriptor type
* @returns {string} The heading
* @memberof ToolTabService
*/
descriptorTypeToHeaderName(descriptorType: ToolDescriptor.TypeEnum): string {
switch (descriptorType) {
case ToolDescriptor.TypeEnum.CWL:
return 'Tool Excerpt';
case ToolDescriptor.TypeEnum.WDL:
return 'Task Excerpt';
case ToolDescriptor.TypeEnum.NFL:
return 'Process Excerpt';
default:
console.error('Unknown descriptor type found: ' + descriptorType);
return 'Tool Excerpt';
}
}

/**
* In the Workflow Excerpt column, each row will have a heading before the tool.id.
* This determines that heading.
*
* @param {ToolDescriptor.TypeEnum} descriptorType The current workflow's descriptor type
* @returns {string} The heading
* @memberof ToolTabService
*/
descriptorTypeToWorkflowExcerptRowHeading(descriptorType: ToolDescriptor.TypeEnum): string {
switch (descriptorType) {
case ToolDescriptor.TypeEnum.CWL:
return 'tool\xa0ID';
case ToolDescriptor.TypeEnum.WDL:
return 'task\xa0ID';
case ToolDescriptor.TypeEnum.NFL:
return 'process\xa0name';
default:
console.error('Unknown descriptor type found: ' + descriptorType);
return 'tool\xa0ID';
}
}
}

2 changes: 1 addition & 1 deletion src/app/workflow/workflow.component.html
Original file line number Diff line number Diff line change
Expand Up @@ -123,7 +123,7 @@ <h3>
<div class="row m-1" *ngIf="!starGazersClicked && !showRedirect">
<div class="col-sm-12 p-0" [ngClass]="{'col-md-10 col-lg-9': isWorkflowPublic}">
<div class="ds-tabs" [ngClass]="{'mr-3': isWorkflowPublic}">
<mat-tab-group [selectedIndex]="selected.value" dynamicHeight class="ds-tabs" id="workflow_tabs" #entryTabs (selectedIndexChange)="selected.setValue($event)" (selectedTabChange)="selectedTabChange($event)" mat-stretch-tabs>
<mat-tab-group [selectedIndex]="selected.value" class="ds-tabs" id="workflow_tabs" #entryTabs (selectedIndexChange)="selected.setValue($event)" (selectedTabChange)="selectedTabChange($event)" mat-stretch-tabs>
<mat-tab id="infoTab" label="Info" id="infoTab">
<app-info-tab [validVersions]="validVersions" [extendedWorkflow]="(extendedWorkflow$ | async)" [defaultVersion]="defaultVersion" [selectedVersion]="selectedVersion" [canRead]="canRead" [canWrite]="canWrite" [isOwner]="isOwner"></app-info-tab>
</mat-tab>
Expand Down

0 comments on commit 001c1ed

Please sign in to comment.