From 7fc8f9c3315b9173e8c3e5e2e0d15e167a2dc26d Mon Sep 17 00:00:00 2001 From: Blackbaud-SteveBrush Date: Thu, 2 Nov 2017 09:17:18 -0400 Subject: [PATCH 1/3] Allow for async heading changes --- .../grid-dynamic.component.fixture.html | 7 +- .../grid-dynamic.component.fixture.ts | 31 ++++-- src/modules/grid/grid-column.component.ts | 36 +++++- src/modules/grid/grid.component.spec.ts | 92 ++++++++++----- src/modules/grid/grid.component.ts | 105 +++++++++++------- 5 files changed, 185 insertions(+), 86 deletions(-) diff --git a/src/modules/grid/fixtures/grid-dynamic.component.fixture.html b/src/modules/grid/fixtures/grid-dynamic.component.fixture.html index b7d23d613..48565536d 100644 --- a/src/modules/grid/fixtures/grid-dynamic.component.fixture.html +++ b/src/modules/grid/fixtures/grid-dynamic.component.fixture.html @@ -2,7 +2,10 @@ [data]="data"> + [heading]="gridColumn.heading"> + + diff --git a/src/modules/grid/fixtures/grid-dynamic.component.fixture.ts b/src/modules/grid/fixtures/grid-dynamic.component.fixture.ts index cc4cf0ef7..fbe02f9b4 100644 --- a/src/modules/grid/fixtures/grid-dynamic.component.fixture.ts +++ b/src/modules/grid/fixtures/grid-dynamic.component.fixture.ts @@ -1,5 +1,7 @@ import { Component } from '@angular/core'; +import { BehaviorSubject } from 'rxjs/BehaviorSubject'; + @Component({ selector: 'sky-test-cmp', templateUrl: './grid-dynamic.component.fixture.html' @@ -7,18 +9,29 @@ import { Component } from '@angular/core'; export class GridDynamicTestComponent { public data: Array; public gridColumns: Array; + public asyncHeading = new BehaviorSubject('default'); + constructor() { - this.data = [{ 'id': 1, 'name': 'Windstorm', 'email': 'windstorm@gmail.com' }, - { 'id': 2, 'name': 'Bombasto', 'email': 'bombasto@gmail.com' }, - { 'id': 3, 'name': 'Magneta', 'email': 'magenta@gmail.com' }, - { 'id': 4, 'name': 'Tornado', 'email': 'tornado@gmail.com' }]; - this.gridColumns = [{ 'id': 1, 'field': 'name', 'heading': 'Name Initial' }, - { 'id': 2, 'field': 'email', 'heading': 'Email Initial' }]; + this.data = [ + { 'id': 1, 'name': 'Windstorm', 'email': 'windstorm@gmail.com' }, + { 'id': 2, 'name': 'Bombasto', 'email': 'bombasto@gmail.com' }, + { 'id': 3, 'name': 'Magneta', 'email': 'magenta@gmail.com' }, + { 'id': 4, 'name': 'Tornado', 'email': 'tornado@gmail.com' } + ]; + this.gridColumns = [ + { 'id': 1, 'field': 'name', 'heading': 'Name Initial' }, + { 'id': 2, 'field': 'email', 'heading': 'Email Initial' } + ]; + + setTimeout(() => { + this.asyncHeading.next('updated'); + }, 500); } public changeColumns() { - this.gridColumns = [{ 'id': 1, 'field': 'name', 'heading': 'Name' }, - { 'id': 2, 'field': 'email', 'heading': 'Email' }]; + this.gridColumns = [ + { 'id': 1, 'field': 'name', 'heading': 'Name' }, + { 'id': 2, 'field': 'email', 'heading': 'Email' } + ]; } - } diff --git a/src/modules/grid/grid-column.component.ts b/src/modules/grid/grid-column.component.ts index 959e6566c..fbceded5a 100644 --- a/src/modules/grid/grid-column.component.ts +++ b/src/modules/grid/grid-column.component.ts @@ -1,10 +1,13 @@ import { + ChangeDetectionStrategy, Component, - Input, ContentChildren, + EventEmitter, + Input, + OnChanges, + SimpleChanges, TemplateRef, - QueryList, - ChangeDetectionStrategy + QueryList } from '@angular/core'; @Component({ @@ -12,23 +15,31 @@ import { template: '', changeDetection: ChangeDetectionStrategy.OnPush }) -export class SkyGridColumnComponent { +export class SkyGridColumnComponent implements OnChanges { @Input() public id: string; + @Input() public heading: string; + @Input() public width: number; + @Input() public hidden: boolean; + @Input() public locked: boolean; + @Input() public field: string; + @Input() public type: string; + @Input() public description: string; + @Input() public isSortable: boolean = true; @@ -40,15 +51,28 @@ export class SkyGridColumnComponent { public templateInput: TemplateRef; /* tslint:enable:no-input-rename */ + public headingChanges: EventEmitter = new EventEmitter(); + @ContentChildren(TemplateRef) private templates: QueryList>; + public ngOnChanges(changes: SimpleChanges) { + if (changes.heading && changes.heading.firstChange === false) { + this.heading = changes.heading.currentValue; + this.headingChanges.emit(this.heading); + } + } + public get template(): TemplateRef { - return (this.templates.length > 0 ? this.templates.first : undefined) || this.templateInput; + if (this.templates.length > 0) { + return this.templates.first; + } + + return this.templateInput; } private search(value: any, searchText: string): boolean { - /* tslint:disable */ + /* tslint:disable:no-null-keyword */ if (value !== undefined && value !== null) { return value.toString().toLowerCase().indexOf(searchText) !== -1; } diff --git a/src/modules/grid/grid.component.spec.ts b/src/modules/grid/grid.component.spec.ts index 79c06da6f..058bc3c0f 100644 --- a/src/modules/grid/grid.component.spec.ts +++ b/src/modules/grid/grid.component.spec.ts @@ -32,12 +32,10 @@ import { let moment = require('moment'); -describe('Grid Component', () => { +fdescribe('Grid Component', () => { function getColumnHeader(id: string, element: DebugElement) { - return element.query( - By.css('th[sky-cmp-id="' + id + '"]') - ); + return element.query(By.css('th[sky-cmp-id="' + id + '"]')); } function getCell(rowId: string, columnId: string, element: DebugElement) { @@ -45,14 +43,14 @@ describe('Grid Component', () => { By.css('tr[sky-cmp-id="' + rowId + '"] sky-grid-cell[sky-cmp-id="' + columnId + '"]') ); } + describe('Basic Fixture', () => { - let component: GridTestComponent, - fixture: ComponentFixture, - nativeElement: HTMLElement, - element: DebugElement; + let component: GridTestComponent; + let fixture: ComponentFixture; + let nativeElement: HTMLElement; + let element: DebugElement; beforeEach(async(() => { - TestBed.configureTestingModule({ imports: [ GridFixturesModule, @@ -68,9 +66,11 @@ describe('Grid Component', () => { function verifyHeaders(useAllHeaders: boolean = false, hiddenCol: boolean = false) { let headerCount = useAllHeaders ? 7 : 5; + if (hiddenCol) { headerCount = 6; } + expect(element.queryAll(By.css('th.sky-grid-heading')).length).toBe(headerCount); expect(getColumnHeader('column1', element).nativeElement.textContent.trim()).toBe('Column1'); expect(getColumnHeader('column2', element).nativeElement.textContent.trim()).toBe('Column2'); @@ -80,7 +80,9 @@ describe('Grid Component', () => { expect(getColumnHeader('column4', element).nativeElement.textContent.trim()) .toBe('Column4'); } + expect(getColumnHeader('column5', element).nativeElement.textContent.trim()).toBe('Column5'); + if (useAllHeaders) { expect(getColumnHeader('hiddenCol1', element).nativeElement.textContent.trim()) .toBe('Column6'); @@ -89,19 +91,17 @@ describe('Grid Component', () => { } } - function verifyData( - flatData: boolean = false, - useAllHeaders: boolean = false, - hiddenCol: boolean = false) { - + function verifyData(flatData = false, useAllHeaders = false, hiddenCol = false) { for (let i = 0; i < component.data.length; i ++) { let row = component.data[i]; let rowData: any; + if (flatData) { rowData = row; } else { rowData = row.data; } + expect(getCell(row.id, 'column1', element).nativeElement.textContent.trim()) .toBe(rowData.column1); expect(getCell(row.id, 'column2', element).nativeElement.textContent.trim()) @@ -110,9 +110,10 @@ describe('Grid Component', () => { .toBe(rowData.column3.toString()); expect(getCell(row.id, 'column3', element) .query(By.css('div.sky-test-custom-template'))).not.toBeNull(); + if (!hiddenCol) { expect(getCell(row.id, 'column4', element).nativeElement.textContent.trim()) - .toBe(rowData.column4.toString()); + .toBe(rowData.column4.toString()); } expect(getCell(row.id, 'column5', element).nativeElement.textContent.trim()) @@ -126,6 +127,7 @@ describe('Grid Component', () => { } } } + describe('standard setup', () => { beforeEach(() => { fixture.detectChanges(); @@ -140,7 +142,7 @@ describe('Grid Component', () => { verifyData(); }); - it('should transform data properly into a usable formate for the grid', () => { + it('should transform data properly into a usable format for the grid', () => { component.data = [ { id: '1', @@ -197,7 +199,6 @@ describe('Grid Component', () => { fixture.detectChanges(); verifyData(true); - }); it('should change displayed headers and data when selected columnids change', () => { @@ -210,6 +211,7 @@ describe('Grid Component', () => { 'hiddenCol1', 'hiddenCol2' ]; + fixture.detectChanges(); verifyHeaders(true); @@ -228,18 +230,21 @@ describe('Grid Component', () => { it('should change styles based on hasToolbar input', () => { expect(element.query(By.css('.sky-grid-table')).nativeElement) .not.toHaveCssClass('sky-grid-has-toolbar'); + component.hasToolbar = true; fixture.detectChanges(); + expect(element.query(By.css('.sky-grid-table')).nativeElement) .toHaveCssClass('sky-grid-has-toolbar'); }); it('should allow the access of search function on displayed columns', () => { - let searchFunctions = component.grid.displayedColumns.map(column => { + const searchFunctions = component.grid.displayedColumns.map(column => { return column.searchFunction; }); expect(searchFunctions.length).toBe(5); + for (let i = 0; i < searchFunctions.length; i++) { let result = searchFunctions[i]('Something', 'something'); expect(result).toBe(true); @@ -252,24 +257,27 @@ describe('Grid Component', () => { component.searchedData = ''; for (let i = 0; i < searchFunctions.length; i++) { - let result = searchFunctions[i]('blaah', 'something'); + const result = searchFunctions[i]('blaah', 'something'); + if (component.searchText !== '') { expect(result).toBe(true); } else { expect(result).toBe(false); } + component.searchText = ''; component.searchedData = ''; - } - for (let i = 0; i < searchFunctions.length; i++) { - let result = searchFunctions[i](undefined, 'something'); + for (let i = 0; i < searchFunctions.length; i++) { + const result = searchFunctions[i](undefined, 'something'); + if (component.searchText !== '') { expect(result).toBe(true); } else { expect(result).toBe(false); } + component.searchText = ''; component.searchedData = ''; } @@ -278,10 +286,12 @@ describe('Grid Component', () => { describe('sorting', () => { it('adds appropriate icons and emits event on click to headers', () => { let headerEl = nativeElement.querySelectorAll('th').item(0) as HTMLElement; + headerEl.click(); fixture.detectChanges(); headerEl = nativeElement.querySelectorAll('th').item(0) as HTMLElement; + expect(component.activeSortSelector) .toEqual({ fieldSelector: 'column1', descending: true}); expect(headerEl.querySelector('i')).toHaveCssClass('fa-caret-down'); @@ -290,6 +300,7 @@ describe('Grid Component', () => { fixture.detectChanges(); headerEl = nativeElement.querySelectorAll('th').item(0) as HTMLElement; + expect(component.activeSortSelector) .toEqual({ fieldSelector: 'column1', descending: false}); expect(headerEl.querySelector('i')).toHaveCssClass('fa-caret-up'); @@ -297,10 +308,12 @@ describe('Grid Component', () => { it('should not respond to click when the appropriate column option is set', () => { let headerEl = nativeElement.querySelectorAll('th').item(1) as HTMLElement; + headerEl.click(); fixture.detectChanges(); headerEl = nativeElement.querySelectorAll('th').item(1) as HTMLElement; + expect(component.activeSortSelector) .toEqual(undefined); expect(headerEl.querySelector('i')).not.toHaveCssClass('fa-caret-down'); @@ -665,12 +678,12 @@ describe('Grid Component', () => { }); describe('Dynamic columns', () => { - it('should handle columns changing after initialization', () => { - let component: GridDynamicTestComponent, - fixture: ComponentFixture, - nativeElement: HTMLElement, - element: DebugElement; + let component: GridDynamicTestComponent, + fixture: ComponentFixture, + nativeElement: HTMLElement, + element: DebugElement; + beforeEach(() => { TestBed.configureTestingModule({ imports: [ GridFixturesModule, @@ -682,10 +695,12 @@ describe('Grid Component', () => { nativeElement = fixture.nativeElement as HTMLElement; element = fixture.debugElement as DebugElement; component = fixture.componentInstance; + }); + it('should handle columns changing after initialization', () => { fixture.detectChanges(); - expect(element.queryAll(By.css('th.sky-grid-heading')).length).toBe(2); + expect(element.queryAll(By.css('th.sky-grid-heading')).length).toBe(3); expect(getColumnHeader('name', element).nativeElement.textContent.trim()) .toBe('Name Initial'); expect(getColumnHeader('email', element).nativeElement.textContent.trim()) @@ -694,12 +709,29 @@ describe('Grid Component', () => { component.changeColumns(); fixture.detectChanges(); - expect(element.queryAll(By.css('th.sky-grid-heading')).length).toBe(2); + expect(element.queryAll(By.css('th.sky-grid-heading')).length).toBe(3); expect(getColumnHeader('name', element).nativeElement.textContent.trim()) .toBe('Name'); expect(getColumnHeader('email', element).nativeElement.textContent.trim()) .toBe('Email'); }); - }); + it('should update columns when async heading changes', fakeAsync((done) => { + fixture.detectChanges(); + + expect(getColumnHeader('asyncColumn', element).nativeElement.textContent.trim()) + .toBe('default'); + + fixture.detectChanges(); + tick(); + fixture.detectChanges(); + + setTimeout(() => { + console.log('hello?????????????????'); + expect(getColumnHeader('asyncColumn', element).nativeElement.textContent.trim()) + .toBe('updated'); + done(); + }, 500); + })); + }); }); diff --git a/src/modules/grid/grid.component.ts b/src/modules/grid/grid.component.ts index a618ccd8c..3b5995d5f 100644 --- a/src/modules/grid/grid.component.ts +++ b/src/modules/grid/grid.component.ts @@ -1,6 +1,7 @@ import { Component, Input, + OnDestroy, Output, ContentChildren, QueryList, @@ -11,18 +12,22 @@ import { EventEmitter, OnChanges } from '@angular/core'; + +import { BehaviorSubject } from 'rxjs/BehaviorSubject'; +import { Observable } from 'rxjs/Observable'; +import { Subscription } from 'rxjs/Subscription'; + import { DragulaService } from 'ng2-dragula/ng2-dragula'; + +import { + ListItemModel, + ListSortFieldSelectorModel +} from '../list/state'; + import { SkyGridColumnComponent } from './grid-column.component'; import { SkyGridColumnModel } from './grid-column.model'; -import { ListItemModel } from '../list/state'; import { SkyGridAdapterService } from './grid-adapter.service'; -import { ListSortFieldSelectorModel } from '../list/state'; - -import { BehaviorSubject } from 'rxjs/BehaviorSubject'; - -import { Observable } from 'rxjs/Observable'; - @Component({ selector: 'sky-grid', templateUrl: './grid.component.html', @@ -33,8 +38,7 @@ import { Observable } from 'rxjs/Observable'; ], changeDetection: ChangeDetectionStrategy.OnPush }) -export class SkyGridComponent implements AfterContentInit, OnChanges { - +export class SkyGridComponent implements AfterContentInit, OnChanges, OnDestroy { @Input() public selectedColumnIds: Array; @@ -65,21 +69,27 @@ export class SkyGridComponent implements AfterContentInit, OnChanges { @Output() public sortFieldChange = new EventEmitter(); - public displayedColumns: Array = new Array(); - - public items: Array = new Array(); + public items: Array; + public displayedColumns: Array; + public currentSortField: BehaviorSubject; - public currentSortField: BehaviorSubject - = new BehaviorSubject({ fieldSelector: '', descending: false }); - - @ContentChildren(SkyGridColumnComponent, {descendants: true}) + @ContentChildren(SkyGridColumnComponent, { descendants: true }) private columnComponents: QueryList; + private subscriptions: Subscription[] = []; + constructor( private dragulaService: DragulaService, private ref: ChangeDetectorRef, private gridAdapter: SkyGridAdapterService - ) {} + ) { + this.displayedColumns = new Array(); + this.items = new Array(); + this.currentSortField = new BehaviorSubject({ + fieldSelector: '', + descending: false + }); + } public ngAfterContentInit() { if (this.columnComponents.length !== 0 || this.columns !== undefined) { @@ -90,22 +100,27 @@ export class SkyGridComponent implements AfterContentInit, OnChanges { } this.transformData(); - this.setDisplayedColumns(true); } - this.columnComponents.changes.subscribe((columnComponents) => { - this.getColumnsFromComponent(); - this.setDisplayedColumns(true); - this.ref.markForCheck(); + // Watch for added/removed columns: + this.subscriptions.push( + this.columnComponents.changes.subscribe(() => this.updateColumns()) + ); + + // Watch for column heading changes: + this.columnComponents.forEach((comp: SkyGridColumnComponent) => { + this.subscriptions.push( + comp.headingChanges.subscribe(() => this.updateColumns()) + ); }); this.gridAdapter.initializeDragAndDrop( - this.dragulaService, - (selectedColumnIds: Array) => { - this.onHeaderDrop(selectedColumnIds); - } - ); + this.dragulaService, + (selectedColumnIds: Array) => { + this.onHeaderDrop(selectedColumnIds); + } + ); } // Do an ngOnChanges where changes to selectedColumnIds and data are watched @@ -125,6 +140,12 @@ export class SkyGridComponent implements AfterContentInit, OnChanges { } } + public ngOnDestroy() { + this.subscriptions.forEach((subscription: Subscription) => { + subscription.unsubscribe(); + }); + } + public sortByColumn(column: SkyGridColumnModel) { if (column.isSortable) { this.currentSortField @@ -158,17 +179,17 @@ export class SkyGridComponent implements AfterContentInit, OnChanges { } private onHeaderDrop(newColumnIds: Array) { - // update selected columnIds - this.selectedColumnIds = newColumnIds; - this.selectedColumnIdsChange.emit(newColumnIds); + // update selected columnIds + this.selectedColumnIds = newColumnIds; + this.selectedColumnIdsChange.emit(newColumnIds); - // set new displayed columns - this.displayedColumns = this.selectedColumnIds.map( - columnId => this.columns.filter(column => column.id === columnId)[0] - ); + // set new displayed columns + this.displayedColumns = this.selectedColumnIds.map( + columnId => this.columns.filter(column => column.id === columnId)[0] + ); - // mark for check because we are using ChangeDetectionStrategy.onPush - this.ref.markForCheck(); + // mark for check because we are using ChangeDetectionStrategy.onPush + this.ref.markForCheck(); } private setDisplayedColumns(respectHidden: boolean = false) { @@ -200,8 +221,14 @@ export class SkyGridComponent implements AfterContentInit, OnChanges { } private getColumnsFromComponent() { - this.columns = this.columnComponents.map(columnComponent => { - return new SkyGridColumnModel(columnComponent.template, columnComponent); - }); + this.columns = this.columnComponents.map(columnComponent => { + return new SkyGridColumnModel(columnComponent.template, columnComponent); + }); + } + + private updateColumns() { + this.getColumnsFromComponent(); + this.setDisplayedColumns(true); + this.ref.markForCheck(); } } From 2c0bded787da359069973a51c8edbef71f4f6e2c Mon Sep 17 00:00:00 2001 From: Blackbaud-SteveBrush Date: Thu, 2 Nov 2017 11:36:10 -0400 Subject: [PATCH 2/3] Updated unit tests --- .../grid-async.component.fixture.html | 7 ++ .../fixtures/grid-async.component.fixture.ts | 25 ++++ .../grid-dynamic.component.fixture.html | 7 +- .../grid-dynamic.component.fixture.ts | 31 ++--- .../grid/fixtures/grid-fixtures.module.ts | 7 +- src/modules/grid/grid.component.spec.ts | 117 ++++++++++-------- 6 files changed, 110 insertions(+), 84 deletions(-) create mode 100644 src/modules/grid/fixtures/grid-async.component.fixture.html create mode 100644 src/modules/grid/fixtures/grid-async.component.fixture.ts diff --git a/src/modules/grid/fixtures/grid-async.component.fixture.html b/src/modules/grid/fixtures/grid-async.component.fixture.html new file mode 100644 index 000000000..ddd3bbcbf --- /dev/null +++ b/src/modules/grid/fixtures/grid-async.component.fixture.html @@ -0,0 +1,7 @@ + + + + diff --git a/src/modules/grid/fixtures/grid-async.component.fixture.ts b/src/modules/grid/fixtures/grid-async.component.fixture.ts new file mode 100644 index 000000000..d3d3cbba5 --- /dev/null +++ b/src/modules/grid/fixtures/grid-async.component.fixture.ts @@ -0,0 +1,25 @@ +/* tslint:disable:no-console */ +import { Component } from '@angular/core'; + +import { BehaviorSubject } from 'rxjs/BehaviorSubject'; + +@Component({ + selector: 'sky-test-cmp', + templateUrl: './grid-async.component.fixture.html' +}) +export class GridAsyncTestComponent { + public items: Array = [ + { 'id': 1, 'name': 'Windstorm', 'email': 'windstorm@gmail.com' }, + { 'id': 2, 'name': 'Bombasto', 'email': 'bombasto@gmail.com' }, + { 'id': 3, 'name': 'Magneta', 'email': 'magenta@gmail.com' }, + { 'id': 4, 'name': 'Tornado', 'email': 'tornado@gmail.com' } + ]; + + public asyncHeading = new BehaviorSubject(undefined); + + constructor() { + setTimeout(() => { + this.asyncHeading.next('updated'); + }, 100); + } +} diff --git a/src/modules/grid/fixtures/grid-dynamic.component.fixture.html b/src/modules/grid/fixtures/grid-dynamic.component.fixture.html index 48565536d..b7d23d613 100644 --- a/src/modules/grid/fixtures/grid-dynamic.component.fixture.html +++ b/src/modules/grid/fixtures/grid-dynamic.component.fixture.html @@ -2,10 +2,7 @@ [data]="data"> - - + [heading]="gridColumn.heading" + > diff --git a/src/modules/grid/fixtures/grid-dynamic.component.fixture.ts b/src/modules/grid/fixtures/grid-dynamic.component.fixture.ts index fbe02f9b4..cc4cf0ef7 100644 --- a/src/modules/grid/fixtures/grid-dynamic.component.fixture.ts +++ b/src/modules/grid/fixtures/grid-dynamic.component.fixture.ts @@ -1,7 +1,5 @@ import { Component } from '@angular/core'; -import { BehaviorSubject } from 'rxjs/BehaviorSubject'; - @Component({ selector: 'sky-test-cmp', templateUrl: './grid-dynamic.component.fixture.html' @@ -9,29 +7,18 @@ import { BehaviorSubject } from 'rxjs/BehaviorSubject'; export class GridDynamicTestComponent { public data: Array; public gridColumns: Array; - public asyncHeading = new BehaviorSubject('default'); - constructor() { - this.data = [ - { 'id': 1, 'name': 'Windstorm', 'email': 'windstorm@gmail.com' }, - { 'id': 2, 'name': 'Bombasto', 'email': 'bombasto@gmail.com' }, - { 'id': 3, 'name': 'Magneta', 'email': 'magenta@gmail.com' }, - { 'id': 4, 'name': 'Tornado', 'email': 'tornado@gmail.com' } - ]; - this.gridColumns = [ - { 'id': 1, 'field': 'name', 'heading': 'Name Initial' }, - { 'id': 2, 'field': 'email', 'heading': 'Email Initial' } - ]; - - setTimeout(() => { - this.asyncHeading.next('updated'); - }, 500); + this.data = [{ 'id': 1, 'name': 'Windstorm', 'email': 'windstorm@gmail.com' }, + { 'id': 2, 'name': 'Bombasto', 'email': 'bombasto@gmail.com' }, + { 'id': 3, 'name': 'Magneta', 'email': 'magenta@gmail.com' }, + { 'id': 4, 'name': 'Tornado', 'email': 'tornado@gmail.com' }]; + this.gridColumns = [{ 'id': 1, 'field': 'name', 'heading': 'Name Initial' }, + { 'id': 2, 'field': 'email', 'heading': 'Email Initial' }]; } public changeColumns() { - this.gridColumns = [ - { 'id': 1, 'field': 'name', 'heading': 'Name' }, - { 'id': 2, 'field': 'email', 'heading': 'Email' } - ]; + this.gridColumns = [{ 'id': 1, 'field': 'name', 'heading': 'Name' }, + { 'id': 2, 'field': 'email', 'heading': 'Email' }]; } + } diff --git a/src/modules/grid/fixtures/grid-fixtures.module.ts b/src/modules/grid/fixtures/grid-fixtures.module.ts index 906ac46db..77fa10ae8 100644 --- a/src/modules/grid/fixtures/grid-fixtures.module.ts +++ b/src/modules/grid/fixtures/grid-fixtures.module.ts @@ -6,12 +6,14 @@ import { SkyGridModule } from '../'; import { GridTestComponent } from './grid.component.fixture'; import { GridEmptyTestComponent } from './grid-empty.component.fixture'; import { GridDynamicTestComponent } from './grid-dynamic.component.fixture'; +import { GridAsyncTestComponent } from './grid-async.component.fixture'; @NgModule({ declarations: [ GridTestComponent, GridEmptyTestComponent, - GridDynamicTestComponent + GridDynamicTestComponent, + GridAsyncTestComponent ], imports: [ CommonModule, @@ -20,7 +22,8 @@ import { GridDynamicTestComponent } from './grid-dynamic.component.fixture'; exports: [ GridTestComponent, GridEmptyTestComponent, - GridDynamicTestComponent + GridDynamicTestComponent, + GridAsyncTestComponent ] }) export class GridFixturesModule { } diff --git a/src/modules/grid/grid.component.spec.ts b/src/modules/grid/grid.component.spec.ts index 058bc3c0f..028cab460 100644 --- a/src/modules/grid/grid.component.spec.ts +++ b/src/modules/grid/grid.component.spec.ts @@ -20,6 +20,9 @@ import { import { GridDynamicTestComponent } from './fixtures/grid-dynamic.component.fixture'; + +import { GridAsyncTestComponent } from './fixtures/grid-async.component.fixture'; + import { SkyGridModule, SkyGridComponent, @@ -32,10 +35,12 @@ import { let moment = require('moment'); -fdescribe('Grid Component', () => { +describe('Grid Component', () => { function getColumnHeader(id: string, element: DebugElement) { - return element.query(By.css('th[sky-cmp-id="' + id + '"]')); + return element.query( + By.css('th[sky-cmp-id="' + id + '"]') + ); } function getCell(rowId: string, columnId: string, element: DebugElement) { @@ -43,14 +48,14 @@ fdescribe('Grid Component', () => { By.css('tr[sky-cmp-id="' + rowId + '"] sky-grid-cell[sky-cmp-id="' + columnId + '"]') ); } - describe('Basic Fixture', () => { - let component: GridTestComponent; - let fixture: ComponentFixture; - let nativeElement: HTMLElement; - let element: DebugElement; + let component: GridTestComponent, + fixture: ComponentFixture, + nativeElement: HTMLElement, + element: DebugElement; beforeEach(async(() => { + TestBed.configureTestingModule({ imports: [ GridFixturesModule, @@ -66,11 +71,9 @@ fdescribe('Grid Component', () => { function verifyHeaders(useAllHeaders: boolean = false, hiddenCol: boolean = false) { let headerCount = useAllHeaders ? 7 : 5; - if (hiddenCol) { headerCount = 6; } - expect(element.queryAll(By.css('th.sky-grid-heading')).length).toBe(headerCount); expect(getColumnHeader('column1', element).nativeElement.textContent.trim()).toBe('Column1'); expect(getColumnHeader('column2', element).nativeElement.textContent.trim()).toBe('Column2'); @@ -80,9 +83,7 @@ fdescribe('Grid Component', () => { expect(getColumnHeader('column4', element).nativeElement.textContent.trim()) .toBe('Column4'); } - expect(getColumnHeader('column5', element).nativeElement.textContent.trim()).toBe('Column5'); - if (useAllHeaders) { expect(getColumnHeader('hiddenCol1', element).nativeElement.textContent.trim()) .toBe('Column6'); @@ -91,17 +92,19 @@ fdescribe('Grid Component', () => { } } - function verifyData(flatData = false, useAllHeaders = false, hiddenCol = false) { + function verifyData( + flatData: boolean = false, + useAllHeaders: boolean = false, + hiddenCol: boolean = false) { + for (let i = 0; i < component.data.length; i ++) { let row = component.data[i]; let rowData: any; - if (flatData) { rowData = row; } else { rowData = row.data; } - expect(getCell(row.id, 'column1', element).nativeElement.textContent.trim()) .toBe(rowData.column1); expect(getCell(row.id, 'column2', element).nativeElement.textContent.trim()) @@ -110,10 +113,9 @@ fdescribe('Grid Component', () => { .toBe(rowData.column3.toString()); expect(getCell(row.id, 'column3', element) .query(By.css('div.sky-test-custom-template'))).not.toBeNull(); - if (!hiddenCol) { expect(getCell(row.id, 'column4', element).nativeElement.textContent.trim()) - .toBe(rowData.column4.toString()); + .toBe(rowData.column4.toString()); } expect(getCell(row.id, 'column5', element).nativeElement.textContent.trim()) @@ -127,7 +129,6 @@ fdescribe('Grid Component', () => { } } } - describe('standard setup', () => { beforeEach(() => { fixture.detectChanges(); @@ -142,7 +143,7 @@ fdescribe('Grid Component', () => { verifyData(); }); - it('should transform data properly into a usable format for the grid', () => { + it('should transform data properly into a usable formate for the grid', () => { component.data = [ { id: '1', @@ -199,6 +200,7 @@ fdescribe('Grid Component', () => { fixture.detectChanges(); verifyData(true); + }); it('should change displayed headers and data when selected columnids change', () => { @@ -211,7 +213,6 @@ fdescribe('Grid Component', () => { 'hiddenCol1', 'hiddenCol2' ]; - fixture.detectChanges(); verifyHeaders(true); @@ -230,21 +231,18 @@ fdescribe('Grid Component', () => { it('should change styles based on hasToolbar input', () => { expect(element.query(By.css('.sky-grid-table')).nativeElement) .not.toHaveCssClass('sky-grid-has-toolbar'); - component.hasToolbar = true; fixture.detectChanges(); - expect(element.query(By.css('.sky-grid-table')).nativeElement) .toHaveCssClass('sky-grid-has-toolbar'); }); it('should allow the access of search function on displayed columns', () => { - const searchFunctions = component.grid.displayedColumns.map(column => { + let searchFunctions = component.grid.displayedColumns.map(column => { return column.searchFunction; }); expect(searchFunctions.length).toBe(5); - for (let i = 0; i < searchFunctions.length; i++) { let result = searchFunctions[i]('Something', 'something'); expect(result).toBe(true); @@ -257,27 +255,24 @@ fdescribe('Grid Component', () => { component.searchedData = ''; for (let i = 0; i < searchFunctions.length; i++) { - const result = searchFunctions[i]('blaah', 'something'); - + let result = searchFunctions[i]('blaah', 'something'); if (component.searchText !== '') { expect(result).toBe(true); } else { expect(result).toBe(false); } - component.searchText = ''; component.searchedData = ''; - } - for (let i = 0; i < searchFunctions.length; i++) { - const result = searchFunctions[i](undefined, 'something'); + } + for (let i = 0; i < searchFunctions.length; i++) { + let result = searchFunctions[i](undefined, 'something'); if (component.searchText !== '') { expect(result).toBe(true); } else { expect(result).toBe(false); } - component.searchText = ''; component.searchedData = ''; } @@ -286,12 +281,10 @@ fdescribe('Grid Component', () => { describe('sorting', () => { it('adds appropriate icons and emits event on click to headers', () => { let headerEl = nativeElement.querySelectorAll('th').item(0) as HTMLElement; - headerEl.click(); fixture.detectChanges(); headerEl = nativeElement.querySelectorAll('th').item(0) as HTMLElement; - expect(component.activeSortSelector) .toEqual({ fieldSelector: 'column1', descending: true}); expect(headerEl.querySelector('i')).toHaveCssClass('fa-caret-down'); @@ -300,7 +293,6 @@ fdescribe('Grid Component', () => { fixture.detectChanges(); headerEl = nativeElement.querySelectorAll('th').item(0) as HTMLElement; - expect(component.activeSortSelector) .toEqual({ fieldSelector: 'column1', descending: false}); expect(headerEl.querySelector('i')).toHaveCssClass('fa-caret-up'); @@ -308,12 +300,10 @@ fdescribe('Grid Component', () => { it('should not respond to click when the appropriate column option is set', () => { let headerEl = nativeElement.querySelectorAll('th').item(1) as HTMLElement; - headerEl.click(); fixture.detectChanges(); headerEl = nativeElement.querySelectorAll('th').item(1) as HTMLElement; - expect(component.activeSortSelector) .toEqual(undefined); expect(headerEl.querySelector('i')).not.toHaveCssClass('fa-caret-down'); @@ -678,12 +668,12 @@ fdescribe('Grid Component', () => { }); describe('Dynamic columns', () => { - let component: GridDynamicTestComponent, - fixture: ComponentFixture, - nativeElement: HTMLElement, - element: DebugElement; + it('should handle columns changing after initialization', () => { + let component: GridDynamicTestComponent, + fixture: ComponentFixture, + nativeElement: HTMLElement, + element: DebugElement; - beforeEach(() => { TestBed.configureTestingModule({ imports: [ GridFixturesModule, @@ -695,12 +685,10 @@ fdescribe('Grid Component', () => { nativeElement = fixture.nativeElement as HTMLElement; element = fixture.debugElement as DebugElement; component = fixture.componentInstance; - }); - it('should handle columns changing after initialization', () => { fixture.detectChanges(); - expect(element.queryAll(By.css('th.sky-grid-heading')).length).toBe(3); + expect(element.queryAll(By.css('th.sky-grid-heading')).length).toBe(2); expect(getColumnHeader('name', element).nativeElement.textContent.trim()) .toBe('Name Initial'); expect(getColumnHeader('email', element).nativeElement.textContent.trim()) @@ -709,29 +697,48 @@ fdescribe('Grid Component', () => { component.changeColumns(); fixture.detectChanges(); - expect(element.queryAll(By.css('th.sky-grid-heading')).length).toBe(3); + expect(element.queryAll(By.css('th.sky-grid-heading')).length).toBe(2); expect(getColumnHeader('name', element).nativeElement.textContent.trim()) .toBe('Name'); expect(getColumnHeader('email', element).nativeElement.textContent.trim()) .toBe('Email'); }); + }); - it('should update columns when async heading changes', fakeAsync((done) => { - fixture.detectChanges(); + describe('async headings', () => { + it('should handle async column headings', (done: any) => { + let component: GridAsyncTestComponent; + let fixture: ComponentFixture; + let nativeElement: HTMLElement; + let element: DebugElement; - expect(getColumnHeader('asyncColumn', element).nativeElement.textContent.trim()) - .toBe('default'); + TestBed.configureTestingModule({ + imports: [ + GridFixturesModule, + SkyGridModule + ] + }); + + fixture = TestBed.createComponent(GridAsyncTestComponent); + nativeElement = fixture.nativeElement as HTMLElement; + element = fixture.debugElement as DebugElement; + component = fixture.componentInstance; fixture.detectChanges(); - tick(); + + expect(getColumnHeader('column1', element).nativeElement.textContent.trim()) + .toBe(''); + fixture.detectChanges(); setTimeout(() => { - console.log('hello?????????????????'); - expect(getColumnHeader('asyncColumn', element).nativeElement.textContent.trim()) - .toBe('updated'); - done(); - }, 500); - })); + fixture.detectChanges(); + fixture.whenStable().then(() => { + expect(getColumnHeader('column1', element).nativeElement.textContent.trim()) + .toBe('updated'); + done(); + }); + }, 110); + }); }); }); From 3de23472c1363fd4ce9983aedc115227976f3463 Mon Sep 17 00:00:00 2001 From: Blackbaud-SteveBrush Date: Thu, 2 Nov 2017 11:43:11 -0400 Subject: [PATCH 3/3] Removed comment --- src/modules/grid/fixtures/grid-async.component.fixture.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/src/modules/grid/fixtures/grid-async.component.fixture.ts b/src/modules/grid/fixtures/grid-async.component.fixture.ts index d3d3cbba5..4d849d2a9 100644 --- a/src/modules/grid/fixtures/grid-async.component.fixture.ts +++ b/src/modules/grid/fixtures/grid-async.component.fixture.ts @@ -1,4 +1,3 @@ -/* tslint:disable:no-console */ import { Component } from '@angular/core'; import { BehaviorSubject } from 'rxjs/BehaviorSubject';