Update styles for stack view.

This commit is contained in:
kunw 2017-06-02 18:16:57 +08:00
parent 4246f55180
commit 9684f146b3
13 changed files with 226 additions and 24 deletions

View File

@ -7,7 +7,7 @@ export const LIST_REPOSITORY_TEMPLATE = `
<clr-dg-action-overflow [hidden]="!hasProjectAdminRole">
<button class="action-item" (click)="deleteRepo(r.name)">{{'REPOSITORY.DELETE' | translate}}</button>
</clr-dg-action-overflow>
<clr-dg-cell>{{r.name}}</clr-dg-cell>
<clr-dg-cell><a href="javascript:void(0)" (click)="gotoLink(projectId || r.project_id, r.name || r.repository_name)">{{r.name}}</a></clr-dg-cell>
<clr-dg-cell>{{r.tags_count}}</clr-dg-cell>
<clr-dg-cell>{{r.pull_count}}</clr-dg-cell>
</clr-dg-row>

View File

@ -2,12 +2,18 @@
import { ComponentFixture, TestBed, async } from '@angular/core/testing';
import { By } from '@angular/platform-browser';
import { DebugElement } from '@angular/core';
import { Router } from '@angular/router';
import { SharedModule } from '../shared/shared.module';
import { ConfirmationDialogComponent } from '../confirmation-dialog/confirmation-dialog.component';
import { ListRepositoryComponent } from './list-repository.component';
import { Repository } from '../service/interface';
class RouterStub {
navigateByUrl(url: string) { return url; }
}
describe('ListRepositoryComponent (inline template)', ()=> {
let comp: ListRepositoryComponent;
@ -43,7 +49,9 @@ describe('ListRepositoryComponent (inline template)', ()=> {
ListRepositoryComponent,
ConfirmationDialogComponent
],
providers: []
providers: [
{ provide: Router, useClass: RouterStub }
]
});
}));

View File

@ -1,4 +1,5 @@
import { Component, Input, Output, EventEmitter, ChangeDetectorRef, ChangeDetectionStrategy } from '@angular/core';
import { Router } from '@angular/router';
import { State, Comparator } from 'clarity-angular';
@ -13,6 +14,7 @@ import { CustomComparator } from '../utils';
changeDetection: ChangeDetectionStrategy.OnPush
})
export class ListRepositoryComponent {
@Input() urlPrefix: string;
@Input() projectId: number;
@Input() repositories: Repository[];
@ -28,6 +30,7 @@ export class ListRepositoryComponent {
tagsCountComparator: Comparator<Repository> = new CustomComparator<Repository>('tags_count', 'number');
constructor(
private router: Router,
private ref: ChangeDetectorRef) {
let hnd = setInterval(()=>ref.markForCheck(), 100);
setTimeout(()=>clearInterval(hnd), 1000);
@ -44,4 +47,9 @@ export class ListRepositoryComponent {
this.paginate.emit(state);
}
}
public gotoLink(projectId: number, repoName: string): void {
let linkUrl = [this.urlPrefix, 'tags', projectId, repoName];
this.router.navigate(linkUrl);
}
}

View File

@ -13,5 +13,14 @@ export const REPOSITORY_STACKVIEW_STYLES: string = `
:host >>> .datagrid .datagrid-body .datagrid-row {
overflow-x: hidden;
overflow-y: hidden;
background-color: #ccc;
}
:host >>> .datagrid-body .datagrid-row .datagrid-row-master{
background-color: #FFFFFF;
}
:host >>> .datagrid .datagrid-placeholder-container {
display: none;
}
`;

View File

@ -10,10 +10,10 @@ export const REPOSITORY_STACKVIEW_TEMPLATE: string = `
</div>
</div>
<div class="col-lg-12 col-md-12 col-sm-12 col-xs-12">
<clr-datagrid (clrDgRefresh)="refresh($event)">
<clr-datagrid>
<clr-dg-column [clrDgField]="'name'">{{'REPOSITORY.NAME' | translate}}</clr-dg-column>
<clr-dg-column>{{'REPOSITORY.TAGS_COUNT' | translate}}</clr-dg-column>
<clr-dg-column>{{'REPOSITORY.PULL_COUNT' | translate}}</clr-dg-column>
<clr-dg-column [clrDgSortBy]="tagsCountComparator">{{'REPOSITORY.TAGS_COUNT' | translate}}</clr-dg-column>
<clr-dg-column [clrDgSortBy]="pullCountComparator">{{'REPOSITORY.PULL_COUNT' | translate}}</clr-dg-column>
<clr-dg-row *clrDgItems="let r of repositories">
<clr-dg-action-overflow [hidden]="!hasProjectAdminRole">
<button class="action-item" (click)="deleteRepo(r.name)">{{'REPOSITORY.DELETE' | translate}}</button>
@ -21,11 +21,11 @@ export const REPOSITORY_STACKVIEW_TEMPLATE: string = `
<clr-dg-cell>{{r.name}}</clr-dg-cell>
<clr-dg-cell>{{r.tags_count}}</clr-dg-cell>
<clr-dg-cell>{{r.pull_count}}</clr-dg-cell>
<hbr-tag style="min-height: 145px;" *clrIfExpanded ngProjectAs="clr-dg-row-detail" class="sub-grid-custom" [repoName]="r.name" [sessionInfo]="sessionInfo" [projectId]="projectId" [isEmbeded]="true" (refreshRepo)="refresh($event)"></hbr-tag>
<hbr-tag *clrIfExpanded ngProjectAs="clr-dg-row-detail" class="sub-grid-custom" [repoName]="r.name" [sessionInfo]="sessionInfo" [projectId]="projectId" [isEmbedded]="true" (refreshRepo)="refresh($event)"></hbr-tag>
</clr-dg-row>
<clr-dg-footer>
{{pagination.firstItem + 1}} - {{pagination.lastItem + 1}} {{'REPOSITORY.OF' | translate}}
{{pagination.totalItems}}{{'REPOSITORY.ITEMS' | translate}}
{{pagination.totalItems}} {{'REPOSITORY.ITEMS' | translate}}
<clr-dg-pagination #pagination [clrDgPageSize]="15"></clr-dg-pagination>
</clr-dg-footer>
</clr-datagrid>

View File

@ -0,0 +1,152 @@
import { ComponentFixture, TestBed, async } from '@angular/core/testing';
import { By } from '@angular/platform-browser';
import { DebugElement } from '@angular/core';
import { SharedModule } from '../shared/shared.module';
import { ConfirmationDialogComponent } from '../confirmation-dialog/confirmation-dialog.component';
import { RepositoryStackviewComponent } from './repository-stackview.component';
import { TagComponent } from '../tag/tag.component';
import { FilterComponent } from '../filter/filter.component';
import { ErrorHandler } from '../error-handler/error-handler';
import { Repository, Tag } from '../service/interface';
import { SERVICE_CONFIG, IServiceConfig } from '../service.config';
import { RepositoryService, RepositoryDefaultService } from '../service/repository.service';
import { TagService, TagDefaultService } from '../service/tag.service';
import { click } from '../utils';
describe('RepositoryComponentStackview (inline template)', ()=> {
let compRepo: RepositoryStackviewComponent;
let fixtureRepo: ComponentFixture<RepositoryStackviewComponent>;
let repositoryService: RepositoryService;
let spyRepos: jasmine.Spy;
let compTag: TagComponent;
let fixtureTag: ComponentFixture<TagComponent>;
let tagService: TagService;
let spyTags: jasmine.Spy;
let mockRepoData: Repository[] = [
{
"id": 1,
"name": "library/busybox",
"project_id": 1,
"description": "",
"pull_count": 0,
"star_count": 0,
"tags_count": 1
},
{
"id": 2,
"name": "library/nginx",
"project_id": 1,
"description": "",
"pull_count": 0,
"star_count": 0,
"tags_count": 1
}
];
let mockTagData: Tag[] = [
{
"digest": "sha256:e5c82328a509aeb7c18c1d7fb36633dc638fcf433f651bdcda59c1cc04d3ee55",
"name": "1.11.5",
"architecture": "amd64",
"os": "linux",
"docker_version": "1.12.3",
"author": "NGINX Docker Maintainers \"docker-maint@nginx.com\"",
"created": new Date("2016-11-08T22:41:15.912313785Z"),
"signature": null
}
];
let config: IServiceConfig = {
repositoryBaseEndpoint: '/api/repository/testing'
};
beforeEach(async(()=>{
TestBed.configureTestingModule({
imports: [
SharedModule
],
declarations: [
RepositoryStackviewComponent,
TagComponent,
ConfirmationDialogComponent,
FilterComponent
],
providers: [
ErrorHandler,
{ provide: SERVICE_CONFIG, useValue : config },
{ provide: RepositoryService, useClass: RepositoryDefaultService },
{ provide: TagService, useClass: TagDefaultService }
]
});
}));
beforeEach(()=>{
fixtureRepo = TestBed.createComponent(RepositoryStackviewComponent);
compRepo = fixtureRepo.componentInstance;
compRepo.projectId = 1;
compRepo.sessionInfo = {
hasProjectAdminRole: true
};
repositoryService = fixtureRepo.debugElement.injector.get(RepositoryService);
spyRepos = spyOn(repositoryService, 'getRepositories').and.returnValues(Promise.resolve(mockRepoData));
fixtureRepo.detectChanges();
});
beforeEach(()=>{
fixtureTag = TestBed.createComponent(TagComponent);
compTag = fixtureTag.componentInstance;
compTag.projectId = compRepo.projectId;
compTag.repoName = 'library/busybox';
compTag.sessionInfo = compRepo.sessionInfo;
tagService = fixtureTag.debugElement.injector.get(TagService);
spyTags = spyOn(tagService, 'getTags').and.returnValues(Promise.resolve(mockTagData));
fixtureTag.detectChanges();
});
it('should load and render data', async(()=>{
fixtureRepo.detectChanges();
fixtureRepo.whenStable().then(()=>{
fixtureRepo.detectChanges();
let deRepo: DebugElement = fixtureRepo.debugElement.query(By.css('datagrid-cell'));
fixtureRepo.detectChanges();
expect(deRepo).toBeTruthy();
let elRepo: HTMLElement = deRepo.nativeElement;
fixtureRepo.detectChanges();
expect(elRepo).toBeTruthy();
fixtureRepo.detectChanges();
expect(elRepo.textContent).toEqual('library/busybox');
click(deRepo);
fixtureTag.detectChanges();
let deTag: DebugElement = fixtureTag.debugElement.query(By.css('datagrid-cell'));
expect(deTag).toBeTruthy();
let elTag: HTMLElement = deTag.nativeElement;
expect(elTag).toBeTruthy();
expect(elTag.textContent).toEqual('1.12.5');
});
}));
it('should filter data by keyword', async(()=>{
fixtureRepo.detectChanges();
fixtureRepo.whenStable().then(()=>{
fixtureRepo.detectChanges();
compRepo.doSearchRepoNames('nginx');
fixtureRepo.detectChanges();
let de: DebugElement[] = fixtureRepo.debugElement.queryAll(By.css('datagrid-cell'));
fixtureRepo.detectChanges();
expect(de).toBeTruthy();
expect(de.length).toEqual(1);
let el: HTMLElement = de[0].nativeElement;
fixtureRepo.detectChanges();
expect(el).toBeTruthy();
expect(el.textContent).toEqual('library/nginx');
});
}));
});

View File

@ -1,6 +1,6 @@
import { Component, Input, OnInit, ViewChild, ChangeDetectionStrategy, ChangeDetectorRef } from '@angular/core';
import { TranslateService } from '@ngx-translate/core';
import { State } from 'clarity-angular';
import { Comparator } from 'clarity-angular';
import { REPOSITORY_STACKVIEW_TEMPLATE } from './repository-stackview.component.html';
import { REPOSITORY_STACKVIEW_STYLES } from './repository-stackview.component.css';
@ -8,7 +8,7 @@ import { REPOSITORY_STACKVIEW_STYLES } from './repository-stackview.component.cs
import { Repository, SessionInfo } from '../service/interface';
import { ErrorHandler } from '../error-handler/error-handler';
import { RepositoryService } from '../service/repository.service';
import { toPromise } from '../utils';
import { toPromise, CustomComparator } from '../utils';
import { ConfirmationState, ConfirmationTargets, ConfirmationButtons } from '../shared/shared.const';
@ -30,9 +30,6 @@ export class RepositoryStackviewComponent implements OnInit {
lastFilteredRepoName: string;
totalPage: number;
totalRecordCount: number;
hasProjectAdminRole: boolean;
repositories: Repository[];
@ -40,6 +37,11 @@ export class RepositoryStackviewComponent implements OnInit {
@ViewChild('confirmationDialog')
confirmationDialog: ConfirmationDialogComponent;
pullCountComparator: Comparator<Repository> = new CustomComparator<Repository>('pull_count', 'number');
tagsCountComparator: Comparator<Repository> = new CustomComparator<Repository>('tags_count', 'number');
constructor(
private errorHandler: ErrorHandler,
private translateService: TranslateService,
@ -77,7 +79,7 @@ export class RepositoryStackviewComponent implements OnInit {
this.retrieve();
}
retrieve(state?: State) {
retrieve() {
toPromise<Repository[]>(this.repositoryService
.getRepositories(this.projectId, this.lastFilteredRepoName))
.then(

View File

@ -10,6 +10,6 @@ export const REPOSITORY_TEMPLATE = `
</div>
</div>
<div class="col-lg-12 col-md-12 col-sm-12 col-xs-12">
<hbr-list-repository [projectId]="projectId" [repositories]="changedRepositories" (delete)="deleteRepo($event)" [hasProjectAdminRole]="hasProjectAdminRole" (paginate)="retrieve($event)"></hbr-list-repository>
<hbr-list-repository [urlPrefix]="urlPrefix" [projectId]="projectId" [repositories]="changedRepositories" (delete)="deleteRepo($event)" [hasProjectAdminRole]="hasProjectAdminRole" (paginate)="retrieve($event)"></hbr-list-repository>
</div>
</div>`;

View File

@ -1,6 +1,7 @@
import { ComponentFixture, TestBed, async } from '@angular/core/testing';
import { By } from '@angular/platform-browser';
import { DebugElement } from '@angular/core';
import { Router } from '@angular/router';
import { SharedModule } from '../shared/shared.module';
import { ConfirmationDialogComponent } from '../confirmation-dialog/confirmation-dialog.component';
@ -13,6 +14,10 @@ import { Repository } from '../service/interface';
import { SERVICE_CONFIG, IServiceConfig } from '../service.config';
import { RepositoryService, RepositoryDefaultService } from '../service/repository.service';
class RouterStub {
navigateByUrl(url: string) { return url; }
}
describe('RepositoryComponent (inline template)', ()=> {
let comp: RepositoryComponent;
@ -59,7 +64,8 @@ describe('RepositoryComponent (inline template)', ()=> {
providers: [
ErrorHandler,
{ provide: SERVICE_CONFIG, useValue : config },
{ provide: RepositoryService, useClass: RepositoryDefaultService }
{ provide: RepositoryService, useClass: RepositoryDefaultService },
{ provide: Router, useClass: RouterStub }
]
});
}));

View File

@ -43,6 +43,7 @@ export class RepositoryComponent implements OnInit {
@Input() projectId: number;
@Input() sessionInfo: SessionInfo;
@Input() urlPrefix: string;
lastFilteredRepoName: string;

View File

@ -2,16 +2,32 @@ export const TAG_STYLE = `
.sub-header-title {
margin: 12px 0;
}
.embeded-datagrid {
width: 98%;
}
.hidden-tag {
display: block; height: 0;
}
:host >>> .datagrid {
margin: 0;
}
:host >>> .datagrid-placeholder {
display: none;
}
:host >>> .datagrid .datagrid-body {
background-color: #eee;
}
:host >>> .datagrid .datagrid-head .datagrid-row {
background-color: #eee;
}
:host >>> .datagrid .datagrid-body .datagrid-row-master {
background-color: #eee;
}
`;

View File

@ -12,8 +12,8 @@ export const TAG_TEMPLATE = `
</div>
</clr-modal>
<h2 *ngIf="!isEmbeded" class="sub-header-title">{{repoName}}</h2>
<clr-datagrid [clrDgLoading]="loading" [class.embeded-datagrid]="isEmbeded">
<h2 *ngIf="!isEmbedded" class="sub-header-title">{{repoName}}</h2>
<clr-datagrid [clrDgLoading]="loading" [class.embeded-datagrid]="isEmbedded">
<clr-dg-column [clrDgField]="'name'">{{'REPOSITORY.TAG' | translate}}</clr-dg-column>
<clr-dg-column>{{'REPOSITORY.PULL_COMMAND' | translate}}</clr-dg-column>
<clr-dg-column *ngIf="withNotary">{{'REPOSITORY.SIGNED' | translate}}</clr-dg-column>
@ -43,9 +43,9 @@ export const TAG_TEMPLATE = `
<clr-dg-cell>{{t.architecture}}</clr-dg-cell>
<clr-dg-cell>{{t.os}}</clr-dg-cell>
</clr-dg-row>
<clr-dg-footer *ngIf="!isEmbeded">
<clr-dg-footer>
{{pagination.firstItem + 1}} - {{pagination.lastItem + 1}} {{'REPOSITORY.OF' | translate}}
{{pagination.totalItems}} {{'REPOSITORY.ITEMS' | translate}}
<clr-dg-pagination #pagination [clrDgPageSize]="10"></clr-dg-pagination>
{{pagination.totalItems}} {{'REPOSITORY.ITEMS' | translate}}&nbsp;&nbsp;&nbsp;&nbsp;
<clr-dg-pagination #pagination [clrDgPageSize]="5"></clr-dg-pagination>
</clr-dg-footer>
</clr-datagrid>`;

View File

@ -43,7 +43,7 @@ export class TagComponent implements OnInit {
@Input() projectId: number;
@Input() repoName: string;
@Input() sessionInfo: SessionInfo;
@Input() isEmbeded: boolean;
@Input() isEmbedded: boolean;
@Output() refreshRepo = new EventEmitter<boolean>();