mirror of https://github.com/abpframework/abp.git
31 changed files with 763 additions and 123 deletions
@ -0,0 +1,39 @@ |
|||||
|
import { Component, OnInit } from '@angular/core'; |
||||
|
|
||||
|
@Component({ |
||||
|
selector: 'abp-loading', |
||||
|
template: ` |
||||
|
<div class="abp-loading"> |
||||
|
<i class="fa fa-spinner fa-pulse abp-spinner"></i> |
||||
|
</div> |
||||
|
`,
|
||||
|
styles: [ |
||||
|
` |
||||
|
.abp-loading { |
||||
|
background: rgba(0, 0, 0, 0.2); |
||||
|
position: absolute; |
||||
|
width: 100%; |
||||
|
height: 100%; |
||||
|
top: 0; |
||||
|
left: 0; |
||||
|
z-index: 1040; |
||||
|
} |
||||
|
|
||||
|
.abp-loading .abp-spinner { |
||||
|
position: absolute; |
||||
|
top: 50%; |
||||
|
left: 50%; |
||||
|
-moz-transform: translateX(-50%) translateY(-50%); |
||||
|
-o-transform: translateX(-50%) translateY(-50%); |
||||
|
-ms-transform: translateX(-50%) translateY(-50%); |
||||
|
-webkit-transform: translateX(-50%) translateY(-50%); |
||||
|
transform: translateX(-50%) translateY(-50%); |
||||
|
} |
||||
|
`,
|
||||
|
], |
||||
|
}) |
||||
|
export class LoadingComponent implements OnInit { |
||||
|
constructor() {} |
||||
|
|
||||
|
ngOnInit() {} |
||||
|
} |
||||
@ -0,0 +1,38 @@ |
|||||
|
<div |
||||
|
class="ui-paginator-bottom ui-paginator ui-widget ui-widget-header ui-unselectable-text ui-helper-clearfix" |
||||
|
> |
||||
|
<a |
||||
|
class="ui-paginator-first ui-paginator-element ui-state-default ui-corner-all" |
||||
|
[class.ui-state-disabled]="value === 1" |
||||
|
tabindex="-1" |
||||
|
(click)="changePage(1)" |
||||
|
><span class="ui-paginator-icon pi pi-step-backward"></span></a |
||||
|
><a |
||||
|
class="ui-paginator-prev ui-paginator-element ui-state-default ui-corner-all" |
||||
|
[class.ui-state-disabled]="value === 1" |
||||
|
tabindex="-1" |
||||
|
(click)="changePage(value - 1)" |
||||
|
><span class="ui-paginator-icon pi pi-caret-left"></span></a |
||||
|
><span class="ui-paginator-pages" |
||||
|
><a |
||||
|
*ngFor="let page of pageArray; trackBy: trackByFn" |
||||
|
(click)="changePage(page)" |
||||
|
class="ui-paginator-page ui-paginator-element ui-state-default ui-corner-all" |
||||
|
[class.ui-state-active]="page === value" |
||||
|
tabindex="0" |
||||
|
>{{ page }}</a |
||||
|
></span |
||||
|
><a |
||||
|
class="ui-paginator-next ui-paginator-element ui-state-default ui-corner-all" |
||||
|
[class.ui-state-disabled]="value === totalPages" |
||||
|
tabindex="0" |
||||
|
(click)="changePage(value + 1)" |
||||
|
><span class="ui-paginator-icon pi pi-caret-right"></span></a |
||||
|
><a |
||||
|
class="ui-paginator-last ui-paginator-element ui-state-default ui-corner-all" |
||||
|
[class.ui-state-disabled]="value === totalPages" |
||||
|
tabindex="0" |
||||
|
(click)="changePage(totalPages)" |
||||
|
><span class="ui-paginator-icon pi pi-step-forward"></span |
||||
|
></a> |
||||
|
</div> |
||||
@ -0,0 +1,52 @@ |
|||||
|
import { Component, Input, OnInit, Output, EventEmitter, TrackByFunction } from '@angular/core'; |
||||
|
|
||||
|
@Component({ |
||||
|
selector: 'abp-pagination', |
||||
|
templateUrl: 'pagination.component.html', |
||||
|
}) |
||||
|
export class PaginationComponent implements OnInit { |
||||
|
private _value = 1; |
||||
|
@Input() |
||||
|
get value(): number { |
||||
|
return this._value; |
||||
|
} |
||||
|
set value(newValue: number) { |
||||
|
if (this._value === newValue) return; |
||||
|
|
||||
|
this._value = newValue; |
||||
|
this.valueChange.emit(newValue); |
||||
|
} |
||||
|
|
||||
|
@Output() |
||||
|
readonly valueChange = new EventEmitter<number>(); |
||||
|
|
||||
|
@Input() |
||||
|
totalPages = 0; |
||||
|
|
||||
|
get pageArray(): number[] { |
||||
|
const count = this.totalPages < 5 ? this.totalPages : 5; |
||||
|
|
||||
|
if (this.value === 1 || this.value === 2) { |
||||
|
return Array.from(new Array(count)).map((_, index) => index + 1); |
||||
|
} else if (this.value === this.totalPages || this.value === this.totalPages - 1) { |
||||
|
return Array.from(new Array(count)).map((_, index) => this.totalPages - count + 1 + index); |
||||
|
} else { |
||||
|
return [this.value - 2, this.value - 1, this.value, this.value + 1, this.value + 2]; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
trackByFn: TrackByFunction<number> = (_, page) => page; |
||||
|
|
||||
|
ngOnInit() { |
||||
|
if (!this.value || this.value < 1 || this.value > this.totalPages) { |
||||
|
this.value = 1; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
changePage(page: number) { |
||||
|
if (page < 1) return; |
||||
|
else if (page > this.totalPages) return; |
||||
|
|
||||
|
this.value = page; |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,78 @@ |
|||||
|
<div #wrapper class="ui-table ui-widget"> |
||||
|
<div class="ui-table-wrapper"> |
||||
|
<ng-container |
||||
|
*ngTemplateOutlet="scrollable ? scrollableTemplate : defaultTemplate" |
||||
|
></ng-container> |
||||
|
<abp-pagination |
||||
|
*ngIf="rows" |
||||
|
[totalPages]="totalPages" |
||||
|
[(value)]="page" |
||||
|
(valueChange)="pageChange.emit($event)" |
||||
|
></abp-pagination> |
||||
|
</div> |
||||
|
</div> |
||||
|
|
||||
|
<ng-template #scrollableTemplate> |
||||
|
<div class="ui-table-scrollable-wrapper"> |
||||
|
<div class="ui-table-scrollable-view"></div> |
||||
|
<div class="ui-table-scrollable-header ui-widget-header"> |
||||
|
<div [style.margin-left.px]="-bodyScrollLeft" class="ui-table-scrollable-header-box"> |
||||
|
<table class="ui-table-scrollable-header-table"> |
||||
|
<ng-container *ngTemplateOutlet="colGroup"></ng-container> |
||||
|
<ng-container *ngTemplateOutlet="head"></ng-container> |
||||
|
<tbody></tbody> |
||||
|
</table> |
||||
|
</div> |
||||
|
</div> |
||||
|
<div |
||||
|
#scrollableBody |
||||
|
(scroll)="bodyScrollLeft = scrollableBody.scrollLeft" |
||||
|
class="ui-table-scrollable-body" |
||||
|
[style.max-height]="scrollHeight" |
||||
|
> |
||||
|
<table class="ui-table-scrollable-body-table"> |
||||
|
<ng-container *ngTemplateOutlet="colGroup"></ng-container> |
||||
|
<ng-container *ngTemplateOutlet="body"></ng-container> |
||||
|
</table> |
||||
|
</div> |
||||
|
</div> |
||||
|
</ng-template> |
||||
|
|
||||
|
<ng-template #defaultTemplate> |
||||
|
<table> |
||||
|
<ng-container *ngTemplateOutlet="colGroup"></ng-container> |
||||
|
<ng-container *ngTemplateOutlet="head"></ng-container> |
||||
|
<ng-container *ngTemplateOutlet="body"></ng-container> |
||||
|
</table> |
||||
|
</ng-template> |
||||
|
|
||||
|
<ng-template #colGroup> |
||||
|
<ng-container *ngTemplateOutlet="colgroupTemplate"></ng-container> |
||||
|
</ng-template> |
||||
|
|
||||
|
<ng-template #head> |
||||
|
<thead class="ui-table-thead"> |
||||
|
<ng-container *ngTemplateOutlet="headerTemplate"></ng-container> |
||||
|
</thead> |
||||
|
</ng-template> |
||||
|
|
||||
|
<ng-template #body> |
||||
|
<tbody class="ui-table-tbody"> |
||||
|
<ng-container *ngIf="value && value.length; else emptyTemplate"> |
||||
|
<ng-template |
||||
|
#bodyTemplateWrapper |
||||
|
*ngFor="let val of slicedValue; let index = index; trackBy: trackByFn" |
||||
|
[ngTemplateOutlet]="bodyTemplate" |
||||
|
[ngTemplateOutletContext]="{ $implicit: val, rowIndex: index }" |
||||
|
></ng-template> |
||||
|
</ng-container> |
||||
|
</tbody> |
||||
|
</ng-template> |
||||
|
|
||||
|
<ng-template #emptyTemplate> |
||||
|
<tr class="empty-row" #emptyRow> |
||||
|
<div [style.width.px]="emptyRow.offsetWidth"> |
||||
|
{{ emptyMessage | abpLocalization }} |
||||
|
</div> |
||||
|
</tr> |
||||
|
</ng-template> |
||||
@ -0,0 +1,106 @@ |
|||||
|
import { |
||||
|
AfterViewInit, |
||||
|
Component, |
||||
|
ElementRef, |
||||
|
EventEmitter, |
||||
|
Input, |
||||
|
Output, |
||||
|
TemplateRef, |
||||
|
TrackByFunction, |
||||
|
ViewChild, |
||||
|
ViewEncapsulation, |
||||
|
} from '@angular/core'; |
||||
|
|
||||
|
@Component({ |
||||
|
selector: 'abp-table', |
||||
|
templateUrl: 'table.component.html', |
||||
|
styles: [ |
||||
|
` |
||||
|
.ui-table .ui-table-tbody > tr:nth-child(even):hover, |
||||
|
.ui-table .ui-table-tbody > tr:hover { |
||||
|
filter: brightness(90%); |
||||
|
} |
||||
|
|
||||
|
.ui-table .ui-table-tbody > tr.empty-row:hover { |
||||
|
filter: none; |
||||
|
} |
||||
|
|
||||
|
.ui-table .ui-table-tbody > tr.empty-row > div { |
||||
|
margin: 10px; |
||||
|
text-align: center; |
||||
|
} |
||||
|
`,
|
||||
|
], |
||||
|
encapsulation: ViewEncapsulation.None, |
||||
|
}) |
||||
|
export class TableComponent { |
||||
|
private _totalRecords: number; |
||||
|
bodyScrollLeft = 0; |
||||
|
|
||||
|
@Input() |
||||
|
value: any[]; |
||||
|
|
||||
|
@Input() |
||||
|
headerTemplate: TemplateRef<any>; |
||||
|
|
||||
|
@Input() |
||||
|
bodyTemplate: TemplateRef<any>; |
||||
|
|
||||
|
@Input() |
||||
|
colgroupTemplate: TemplateRef<any>; |
||||
|
|
||||
|
@Input() |
||||
|
scrollHeight: string; |
||||
|
|
||||
|
@Input() |
||||
|
scrollable: boolean; |
||||
|
|
||||
|
@Input() |
||||
|
rows: number; |
||||
|
|
||||
|
@Input() |
||||
|
page = 1; |
||||
|
|
||||
|
@Input() |
||||
|
trackingProp = 'id'; |
||||
|
|
||||
|
@Input() |
||||
|
emptyMessage = 'AbpAccount::NoDataAvailableInDatatable'; |
||||
|
|
||||
|
@Output() |
||||
|
readonly pageChange = new EventEmitter<number>(); |
||||
|
|
||||
|
@ViewChild('wrapper', { read: ElementRef, static: false }) |
||||
|
wrapperRef: ElementRef<HTMLDivElement>; |
||||
|
|
||||
|
@Input() |
||||
|
get totalRecords(): number { |
||||
|
return this._totalRecords || this.value.length; |
||||
|
} |
||||
|
set totalRecords(newValue: number) { |
||||
|
if (newValue < 0) this._totalRecords = 0; |
||||
|
|
||||
|
this._totalRecords = newValue; |
||||
|
} |
||||
|
|
||||
|
get totalPages(): number { |
||||
|
if (!this.rows) { |
||||
|
return; |
||||
|
} |
||||
|
|
||||
|
return Math.ceil(this.totalRecords / this.rows); |
||||
|
} |
||||
|
|
||||
|
get slicedValue(): any[] { |
||||
|
if (!this.rows || this.rows >= this.value.length) { |
||||
|
return this.value; |
||||
|
} |
||||
|
|
||||
|
const start = (this.page - 1) * this.rows; |
||||
|
return this.value.slice(start, start + this.rows); |
||||
|
} |
||||
|
|
||||
|
trackByFn: TrackByFunction<any> = (_, value) => { |
||||
|
return typeof value === 'object' ? value[this.trackingProp] || value : value; |
||||
|
}; |
||||
|
} |
||||
@ -1 +1,2 @@ |
|||||
|
export * from './loading.directive'; |
||||
export * from './table-sort.directive'; |
export * from './table-sort.directive'; |
||||
|
|||||
@ -0,0 +1,74 @@ |
|||||
|
import { |
||||
|
Directive, |
||||
|
ElementRef, |
||||
|
AfterViewInit, |
||||
|
ViewContainerRef, |
||||
|
ComponentFactoryResolver, |
||||
|
Input, |
||||
|
Injector, |
||||
|
ComponentRef, |
||||
|
ComponentFactory, |
||||
|
HostBinding, |
||||
|
EmbeddedViewRef, |
||||
|
Renderer2, |
||||
|
OnInit, |
||||
|
} from '@angular/core'; |
||||
|
import { LoadingComponent } from '../components/loading/loading.component'; |
||||
|
|
||||
|
@Directive({ selector: '[abpLoading]' }) |
||||
|
export class LoadingDirective implements OnInit { |
||||
|
private _loading: boolean; |
||||
|
|
||||
|
@HostBinding('style.position') |
||||
|
position = 'relative'; |
||||
|
|
||||
|
@Input('abpLoading') |
||||
|
get loading(): boolean { |
||||
|
return this._loading; |
||||
|
} |
||||
|
|
||||
|
set loading(newValue: boolean) { |
||||
|
setTimeout(() => { |
||||
|
if (!this.componentRef) { |
||||
|
this.componentRef = this.cdRes |
||||
|
.resolveComponentFactory(LoadingComponent) |
||||
|
.create(this.injector); |
||||
|
} |
||||
|
|
||||
|
if (newValue && !this.rootNode) { |
||||
|
this.rootNode = (this.componentRef.hostView as EmbeddedViewRef<any>).rootNodes[0]; |
||||
|
this.targetElement.appendChild(this.rootNode); |
||||
|
} else { |
||||
|
this.renderer.removeChild(this.rootNode.parentElement, this.rootNode); |
||||
|
this.rootNode = null; |
||||
|
} |
||||
|
|
||||
|
this._loading = newValue; |
||||
|
}, 0); |
||||
|
} |
||||
|
|
||||
|
@Input('abpLoadingTargetElement') |
||||
|
targetElement: HTMLElement; |
||||
|
|
||||
|
componentRef: ComponentRef<LoadingComponent>; |
||||
|
rootNode: HTMLDivElement; |
||||
|
|
||||
|
constructor( |
||||
|
private elRef: ElementRef<HTMLElement>, |
||||
|
private vcRef: ViewContainerRef, |
||||
|
private cdRes: ComponentFactoryResolver, |
||||
|
private injector: Injector, |
||||
|
private renderer: Renderer2, |
||||
|
) {} |
||||
|
|
||||
|
ngOnInit() { |
||||
|
if (!this.targetElement) { |
||||
|
const { offsetHeight, offsetWidth } = this.elRef.nativeElement; |
||||
|
if (!offsetHeight && !offsetWidth && this.elRef.nativeElement.children.length) { |
||||
|
this.targetElement = this.elRef.nativeElement.children[0] as HTMLElement; |
||||
|
} else { |
||||
|
this.targetElement = this.elRef.nativeElement; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,82 @@ |
|||||
|
import { SpectatorDirective, createDirectiveFactory } from '@ngneat/spectator/jest'; |
||||
|
import { LoadingDirective } from '../directives'; |
||||
|
import { LoadingComponent } from '../components'; |
||||
|
|
||||
|
import { Component } from '@angular/core'; |
||||
|
|
||||
|
@Component({ |
||||
|
selector: 'abp-dummy', |
||||
|
template: '<div id="dummy">Testing Loading Directive</div>', |
||||
|
}) |
||||
|
export class DummyComponent {} |
||||
|
|
||||
|
describe('LoadingDirective', () => { |
||||
|
let spectator: SpectatorDirective<LoadingDirective>; |
||||
|
const createDirective = createDirectiveFactory({ |
||||
|
directive: LoadingDirective, |
||||
|
declarations: [LoadingComponent, DummyComponent], |
||||
|
entryComponents: [LoadingComponent], |
||||
|
}); |
||||
|
|
||||
|
describe('default', () => { |
||||
|
beforeEach(() => { |
||||
|
spectator = createDirective('<div [abpLoading]="status">Testing Loading Directive</div>', { |
||||
|
hostProps: { status: true }, |
||||
|
}); |
||||
|
}); |
||||
|
|
||||
|
it('should create the loading component', done => { |
||||
|
setTimeout(() => { |
||||
|
expect(spectator.directive.rootNode).toBeTruthy(); |
||||
|
expect(spectator.directive.componentRef).toBeTruthy(); |
||||
|
done(); |
||||
|
}, 0); |
||||
|
}); |
||||
|
}); |
||||
|
|
||||
|
describe('with custom target', () => { |
||||
|
const mockTarget = document.createElement('div'); |
||||
|
const spy = jest.spyOn(mockTarget, 'appendChild'); |
||||
|
|
||||
|
beforeEach(() => { |
||||
|
spectator = createDirective( |
||||
|
'<div [abpLoading]="status" [abpLoadingTargetElement]="target">Testing Loading Directive</div>', |
||||
|
{ |
||||
|
hostProps: { status: true, target: mockTarget }, |
||||
|
}, |
||||
|
); |
||||
|
}); |
||||
|
|
||||
|
it('should add the loading component to the DOM', done => { |
||||
|
setTimeout(() => { |
||||
|
expect(spy).toHaveBeenCalled(); |
||||
|
done(); |
||||
|
}, 0); |
||||
|
}); |
||||
|
|
||||
|
it('should remove the loading component to the DOM', done => { |
||||
|
const rendererSpy = jest.spyOn(spectator.directive['renderer'], 'removeChild'); |
||||
|
spectator.setHostInput({ status: false }); |
||||
|
setTimeout(() => { |
||||
|
expect(rendererSpy).toHaveBeenCalled(); |
||||
|
expect(spectator.directive.rootNode).toBeFalsy(); |
||||
|
done(); |
||||
|
}, 0); |
||||
|
}); |
||||
|
}); |
||||
|
|
||||
|
describe('with a component selector', () => { |
||||
|
beforeEach(() => { |
||||
|
spectator = createDirective('<abp-dummy [abpLoading]="status"></abp-dummy>', { |
||||
|
hostProps: { status: true }, |
||||
|
}); |
||||
|
}); |
||||
|
|
||||
|
it('should select the child element', done => { |
||||
|
setTimeout(() => { |
||||
|
expect(spectator.directive.targetElement.id).toBe('dummy'); |
||||
|
done(); |
||||
|
}, 0); |
||||
|
}); |
||||
|
}); |
||||
|
}); |
||||
@ -0,0 +1,63 @@ |
|||||
|
import { createHostFactory, SpectatorHost } from '@ngneat/spectator/jest'; |
||||
|
import { PaginationComponent } from '../components'; |
||||
|
|
||||
|
describe('PaginationComponent', () => { |
||||
|
let spectator: SpectatorHost<PaginationComponent>; |
||||
|
const createHost = createHostFactory({ |
||||
|
component: PaginationComponent, |
||||
|
}); |
||||
|
|
||||
|
beforeEach(() => { |
||||
|
spectator = createHost( |
||||
|
'<abp-pagination [totalPages]="totalPages" [value]="value"></abp-pagination>', |
||||
|
{ |
||||
|
hostProps: { |
||||
|
value: 5, |
||||
|
totalPages: 12, |
||||
|
}, |
||||
|
}, |
||||
|
); |
||||
|
}); |
||||
|
|
||||
|
it('should add ui-state-active class to current page', () => { |
||||
|
expect(spectator.query('.ui-state-active').textContent).toBe('5'); |
||||
|
}); |
||||
|
|
||||
|
it('should display the correct pages', () => { |
||||
|
expect(spectator.queryAll('.ui-paginator-page').map(node => node.textContent)).toEqual([ |
||||
|
'3', |
||||
|
'4', |
||||
|
'5', |
||||
|
'6', |
||||
|
'7', |
||||
|
]); |
||||
|
|
||||
|
spectator.click('.ui-paginator-first'); |
||||
|
|
||||
|
expect(spectator.queryAll('.ui-paginator-page').map(node => node.textContent)).toEqual([ |
||||
|
'1', |
||||
|
'2', |
||||
|
'3', |
||||
|
'4', |
||||
|
'5', |
||||
|
]); |
||||
|
|
||||
|
spectator.setHostInput({ value: 12 }); |
||||
|
|
||||
|
expect(spectator.queryAll('.ui-paginator-page').map(node => node.textContent)).toEqual([ |
||||
|
'8', |
||||
|
'9', |
||||
|
'10', |
||||
|
'11', |
||||
|
'12', |
||||
|
]); |
||||
|
|
||||
|
spectator.setHostInput({ value: 1, totalPages: 3 }); |
||||
|
|
||||
|
expect(spectator.queryAll('.ui-paginator-page').map(node => node.textContent)).toEqual([ |
||||
|
'1', |
||||
|
'2', |
||||
|
'3', |
||||
|
]); |
||||
|
}); |
||||
|
}); |
||||
@ -0,0 +1,74 @@ |
|||||
|
import { createHostFactory, SpectatorHost } from '@ngneat/spectator/jest'; |
||||
|
import { PaginationComponent, TableComponent } from '../components'; |
||||
|
|
||||
|
import { Pipe, PipeTransform } from '@angular/core'; |
||||
|
|
||||
|
@Pipe({ |
||||
|
name: 'abpLocalization', |
||||
|
}) |
||||
|
export class DummyLocalizationPipe implements PipeTransform { |
||||
|
transform(value: any, ...args: any[]): any { |
||||
|
return value; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
describe('TableComponent', () => { |
||||
|
let spectator: SpectatorHost<TableComponent>; |
||||
|
const createHost = createHostFactory({ |
||||
|
component: TableComponent, |
||||
|
declarations: [PaginationComponent, DummyLocalizationPipe], |
||||
|
}); |
||||
|
|
||||
|
describe('without value', () => { |
||||
|
beforeEach(() => { |
||||
|
spectator = createHost( |
||||
|
`<abp-table
|
||||
|
[headerTemplate]="header" |
||||
|
[colgroupTemplate]="colgroup" |
||||
|
[value]="value"> |
||||
|
</abp-table> |
||||
|
<ng-template #colgroup><colgroup><col /></colgroup></ng-template> |
||||
|
<ng-template #header><th>name</th></ng-template>`, |
||||
|
{ |
||||
|
hostProps: { |
||||
|
value: [], |
||||
|
}, |
||||
|
}, |
||||
|
); |
||||
|
}); |
||||
|
|
||||
|
it('should display the empty message', () => { |
||||
|
expect(spectator.query('tr.empty-row>div')).toHaveText( |
||||
|
'AbpAccount::NoDataAvailableInDatatable', |
||||
|
); |
||||
|
}); |
||||
|
|
||||
|
it('should display the header', () => { |
||||
|
expect(spectator.query('thead')).toBeTruthy(); |
||||
|
expect(spectator.query('th')).toHaveText('name'); |
||||
|
}); |
||||
|
|
||||
|
it('should place the colgroup template', () => { |
||||
|
expect(spectator.query('colgroup')).toBeTruthy(); |
||||
|
expect(spectator.query('col')).toBeTruthy(); |
||||
|
}); |
||||
|
}); |
||||
|
|
||||
|
describe('with value', () => { |
||||
|
// TODO
|
||||
|
beforeEach(() => { |
||||
|
spectator = createHost( |
||||
|
`<abp-table
|
||||
|
[headerTemplate]="header" |
||||
|
[value]="value"></abp-table> |
||||
|
<ng-template #header><th>name</th></ng-template> |
||||
|
`,
|
||||
|
{ |
||||
|
hostProps: { |
||||
|
value: [], |
||||
|
}, |
||||
|
}, |
||||
|
); |
||||
|
}); |
||||
|
}); |
||||
|
}); |
||||
Loading…
Reference in new issue