diff options
author | 杨宇千 <crupest@outlook.com> | 2019-02-22 15:24:26 +0000 |
---|---|---|
committer | 杨宇千 <crupest@outlook.com> | 2019-02-22 15:24:26 +0000 |
commit | fe5838a966a314b72ecb4d992f86f5fce635a96f (patch) | |
tree | 6d00a55b9e5f98315384f33aaf5bfc5a60645a0d /Timeline/ClientApp/src | |
parent | 25e606b67506b3bc0242414aebe472f13a02bd9c (diff) | |
parent | e236b5064cd62f40bc910fafe48ac4b9701a4bcd (diff) | |
download | timeline-fe5838a966a314b72ecb4d992f86f5fce635a96f.tar.gz timeline-fe5838a966a314b72ecb4d992f86f5fce635a96f.tar.bz2 timeline-fe5838a966a314b72ecb4d992f86f5fce635a96f.zip |
Merged PR 5: Develop link feature on todo page.
Develop link feature on todo page.
Related work items: #1, #3
Diffstat (limited to 'Timeline/ClientApp/src')
5 files changed, 77 insertions, 41 deletions
diff --git a/Timeline/ClientApp/src/app/todo-list-page/todo-list-page.component.css b/Timeline/ClientApp/src/app/todo-list-page/todo-list-page.component.css index 6a603863..89a0f0ce 100644 --- a/Timeline/ClientApp/src/app/todo-list-page/todo-list-page.component.css +++ b/Timeline/ClientApp/src/app/todo-list-page/todo-list-page.component.css @@ -2,20 +2,15 @@ align-self: flex-end; } -.first-item-box { - justify-content: space-between; -} - .item-box { display: flex; width: 100%; - padding: 10px; + padding: 5px; box-sizing: border-box; } -.sample-box { - box-sizing: border-box; - align-self: flex-start; +.first-item-box { + justify-content: space-between; } .item-id { @@ -26,13 +21,6 @@ height: 1.2rem; } -.item-id-sample { - display: inline-block; - border-radius: 0.2em; - width: 1em; - height: 1em; -} - .item-id-open { background: red; } @@ -40,3 +28,26 @@ .item-id-closed { background: green; } + +.item-title { + margin-left: 5px; + color: black; + text-decoration: none; +} + +.sample-box { + box-sizing: border-box; + align-self: flex-start; +} + +.sample-container { + display: flex; + align-items: center; +} + +.item-id-sample { + border-radius: 0.2em; + width: 1em; + height: 1em; + margin-right: 2px; +} diff --git a/Timeline/ClientApp/src/app/todo-list-page/todo-list-page.component.html b/Timeline/ClientApp/src/app/todo-list-page/todo-list-page.component.html index ce6eb2ed..4e1aa2f1 100644 --- a/Timeline/ClientApp/src/app/todo-list-page/todo-list-page.component.html +++ b/Timeline/ClientApp/src/app/todo-list-page/todo-list-page.component.html @@ -3,19 +3,20 @@ <mat-list> <mat-list-item *ngFor="let item of items; let i = index" style="height:unset;"> <div class="item-box" [class.first-item-box]="i === 0"> - <mat-card [class.align-self-bottom]="i === 0"> + <mat-card class="mat-elevation-z2" [class.align-self-bottom]="i === 0"> <span class="item-id" [class.item-id-closed]="item.closed" [class.item-id-open]="!item.closed">{{item.id}}</span> - {{item.title}} + <a class="mat-h3 item-title" [href]="item.detailUrl">{{item.title}}</a> </mat-card> <div class="sample-box" *ngIf="i === 0"> - <div class="mat-caption"> - <span class="item-id-sample item-id-open"></span> means working now. - </div> - <div class="mat-caption"> - <span class="item-id-sample item-id-closed"></span> means completed. - </div> + <div class="mat-caption sample-container"> + <span class="item-id-sample item-id-open"></span> means working now. </div> + <div class="mat-caption sample-container"> + <span class="item-id-sample item-id-closed"></span> means completed. + </div> + <div class="mat-caption">click on item to see details.</div> + </div> </div> </mat-list-item> </mat-list> diff --git a/Timeline/ClientApp/src/app/todo-list-page/todo-list-page.component.spec.ts b/Timeline/ClientApp/src/app/todo-list-page/todo-list-page.component.spec.ts index e01e91fb..2da74002 100644 --- a/Timeline/ClientApp/src/app/todo-list-page/todo-list-page.component.spec.ts +++ b/Timeline/ClientApp/src/app/todo-list-page/todo-list-page.component.spec.ts @@ -8,8 +8,8 @@ import { TodoListService, WorkItem } from './todo-list.service'; import { By } from '@angular/platform-browser'; @Component({ -/* tslint:disable-next-line:component-selector*/ -selector: 'mat-progress-bar', + /* tslint:disable-next-line:component-selector*/ + selector: 'mat-progress-bar', template: '' }) class MatProgressBarStubComponent { @@ -24,14 +24,18 @@ describe('TodoListPageComponent', () => { let component: TodoListPageComponent; let fixture: ComponentFixture<TodoListPageComponent>; + let mockWorkItems: WorkItem[]; + beforeEach(async(() => { const todoListService: jasmine.SpyObj<TodoListService> = jasmine.createSpyObj('TodoListService', ['getWorkItemList']); - todoListService.getWorkItemList.and.returnValue(asyncData(<WorkItem[]>[{ - id: 0, title: 'Test title 1', closed: true + mockWorkItems = [{ + id: 0, title: 'Test title 1', closed: true, detailUrl: 'https://test.org/workitems/0' }, { - id: 1, title: 'Test title 2', closed: false - }])); + id: 1, title: 'Test title 2', closed: false, detailUrl: 'https://test.org/workitems/1' + }]; + + todoListService.getWorkItemList.and.returnValue(asyncData(mockWorkItems)); TestBed.configureTestingModule({ declarations: [TodoListPageComponent, MatProgressBarStubComponent], @@ -64,4 +68,14 @@ describe('TodoListPageComponent', () => { fixture.detectChanges(); expect(fixture.debugElement.query(By.css('mat-progress-bar'))).toBeFalsy(); })); + + it('should set href on item title', fakeAsync(() => { + fixture.detectChanges(); + tick(); + fixture.detectChanges(); + + fixture.debugElement.queryAll(By.css('a.item-title')).forEach((element, index) => { + expect(element.properties['href']).toBe(mockWorkItems[index].detailUrl); + }); + })); }); diff --git a/Timeline/ClientApp/src/app/todo-list-page/todo-list.service.spec.ts b/Timeline/ClientApp/src/app/todo-list-page/todo-list.service.spec.ts index 7e88ca52..3ed4e004 100644 --- a/Timeline/ClientApp/src/app/todo-list-page/todo-list.service.spec.ts +++ b/Timeline/ClientApp/src/app/todo-list-page/todo-list.service.spec.ts @@ -19,14 +19,26 @@ describe('TodoListServiceService', () => { const service: TodoListService = TestBed.get(TodoListService); expect(service).toBeTruthy(); + const mockAccessInfo: AzureDevOpsAccessInfo = { + username: 'testusername', + personalAccessToken: 'testtoken', + organization: 'testorganization', + project: 'testproject' + }; + + const generateDetailUrl = (id: number) => + `https://dev.azure.com/${mockAccessInfo.organization}/${mockAccessInfo.project}/_workitems/edit/${id}/`; + const mockWorkItems: WorkItem[] = [{ id: 0, title: 'Test work item 1', - closed: true + closed: true, + detailUrl: generateDetailUrl(0) }, { id: 1, title: 'Test work item 2', - closed: false + closed: false, + detailUrl: generateDetailUrl(1) }]; service.getWorkItemList().subscribe(data => { @@ -35,12 +47,7 @@ describe('TodoListServiceService', () => { const httpController: HttpTestingController = TestBed.get(HttpTestingController); - const mockAccessInfo: AzureDevOpsAccessInfo = { - username: 'testusername', - personalAccessToken: 'testtoken', - organization: 'testorganization', - project: 'testproject' - }; + httpController.expectOne('/api/TodoPage/AzureDevOpsAccessInfo').flush(mockAccessInfo); diff --git a/Timeline/ClientApp/src/app/todo-list-page/todo-list.service.ts b/Timeline/ClientApp/src/app/todo-list-page/todo-list.service.ts index 619e9a6b..ed28bc59 100644 --- a/Timeline/ClientApp/src/app/todo-list-page/todo-list.service.ts +++ b/Timeline/ClientApp/src/app/todo-list-page/todo-list.service.ts @@ -29,6 +29,7 @@ export interface WorkItem { id: number; title: string; closed: boolean; + detailUrl: string; } @Injectable({ @@ -41,20 +42,21 @@ export class TodoListService { constructor(private client: HttpClient) { } - private getAzureDevOpsPat(): Observable<AzureDevOpsAccessInfo> { + private getAzureDevOpsAccessInfo(): Observable<AzureDevOpsAccessInfo> { return this.client.get<AzureDevOpsAccessInfo>('/api/TodoPage/AzureDevOpsAccessInfo'); } getWorkItemList(): Observable<WorkItem[]> { - return this.getAzureDevOpsPat().pipe( + return this.getAzureDevOpsAccessInfo().pipe( switchMap( accessInfo => { + const baseUrl = `https://dev.azure.com/${accessInfo.organization}/${accessInfo.project}/`; const headers = new HttpHeaders({ 'Accept': 'application/json', 'Authorization': `Basic ${btoa(accessInfo.username + ':' + accessInfo.personalAccessToken)}` }); return this.client.post<WiqlResult>( - `https://dev.azure.com/${accessInfo.organization}/${accessInfo.project}/_apis/wit/wiql?api-version=5.0`, { + `${baseUrl}_apis/wit/wiql?api-version=5.0`, { query: 'SELECT [System.Id] FROM workitems WHERE [System.TeamProject] = @project' }, { headers: headers }).pipe( switchMap(result => result.workItems), @@ -62,7 +64,8 @@ export class TodoListService { map(result => <WorkItem>{ id: result.id, title: <string>result.fields[TodoListService.titleFieldName], - closed: ((<string>result.fields[TodoListService.stateFieldName]).toLowerCase() === 'closed') + closed: ((<string>result.fields[TodoListService.stateFieldName]).toLowerCase() === 'closed'), + detailUrl: `${baseUrl}_workitems/edit/${result.id}/` }), toArray() ); |