From b84e1b8e55173a05c696966fdc08bc63e3573659 Mon Sep 17 00:00:00 2001 From: Trevor Burch Date: Wed, 15 Aug 2018 16:17:33 -0400 Subject: [PATCH] List view grid column change event (#1897) Added event for watching for column change events on a list view gird. Also fixed issue where grids were not emitting their event at the correct time. Resolves #1092 --- src/modules/grid/grid.component.spec.ts | 16 +++- src/modules/grid/grid.component.ts | 3 + ...ist-view-grid-dynamic.component.fixture.ts | 19 +++- .../list-view-grid.component.spec.ts | 71 +++++++++++---- .../list-view-grid.component.ts | 86 ++++++++++++------- 5 files changed, 141 insertions(+), 54 deletions(-) diff --git a/src/modules/grid/grid.component.spec.ts b/src/modules/grid/grid.component.spec.ts index a8fabe5fe..8e2bd939f 100644 --- a/src/modules/grid/grid.component.spec.ts +++ b/src/modules/grid/grid.component.spec.ts @@ -207,7 +207,19 @@ describe('Grid Component', () => { }); - it('should change displayed headers and data when selected columnids change', () => { + it('should change displayed headers and data when selected columnids change and emit the change event', async(() => { + component.grid.selectedColumnIdsChange.subscribe((newSelectedColumnIds: string[]) => { + expect(newSelectedColumnIds).toEqual([ + 'column1', + 'column2', + 'column3', + 'column4', + 'column5', + 'hiddenCol1', + 'hiddenCol2' + ]); + }); + component.selectedColumnIds = [ 'column1', 'column2', @@ -221,7 +233,7 @@ describe('Grid Component', () => { verifyHeaders(true); verifyData(false, true); - }); + })); it('should show all columns when selectedColumnIds is undefined', () => { component.selectedColumnIds = undefined; diff --git a/src/modules/grid/grid.component.ts b/src/modules/grid/grid.component.ts index 37983c362..183294ddd 100644 --- a/src/modules/grid/grid.component.ts +++ b/src/modules/grid/grid.component.ts @@ -139,6 +139,9 @@ export class SkyGridComponent implements AfterContentInit, OnChanges, OnDestroy this.setDisplayedColumns(true); } else if (changes['selectedColumnIds'] && this.columns) { this.setDisplayedColumns(); + if (changes['selectedColumnIds'].previousValue !== changes['selectedColumnIds'].currentValue) { + this.selectedColumnIdsChange.emit(this.selectedColumnIds); + } } if (changes['data'] && this.data) { diff --git a/src/modules/list-view-grid/fixtures/list-view-grid-dynamic.component.fixture.ts b/src/modules/list-view-grid/fixtures/list-view-grid-dynamic.component.fixture.ts index e4c034459..be6f84468 100644 --- a/src/modules/list-view-grid/fixtures/list-view-grid-dynamic.component.fixture.ts +++ b/src/modules/list-view-grid/fixtures/list-view-grid-dynamic.component.fixture.ts @@ -1,10 +1,14 @@ -import { Component } from '@angular/core'; +import { Component, ViewChild } from '@angular/core'; +import { SkyListViewGridComponent } from '../list-view-grid.component'; @Component({ selector: 'sky-test-cmp', template: require('./list-view-grid-dynamic.component.fixture.html') }) export class ListViewGridDynamicTestComponent { + + @ViewChild(SkyListViewGridComponent) + public grid: SkyListViewGridComponent; public data: Array; public gridColumns: Array; constructor() { @@ -22,7 +26,16 @@ export class ListViewGridDynamicTestComponent { { 'id': 2, 'field': 'email', 'heading': 'Email' }]; } - public changeColumnsDifferent() { - this.gridColumns = [{ 'id': 3, 'field': 'other', 'heading': 'Other' }]; + public changeColumnsNameAndOther() { + this.gridColumns = [{ 'id': 1, 'field': 'name', 'heading': 'Name' }, { 'id': 3, 'field': 'other', 'heading': 'Other' }]; + } + + public changeColumnsOther() { + this.gridColumns = [{ 'id': 3, 'field': 'other', 'heading': 'Other' }]; +} + + public changeColumnsSame() { + this.gridColumns = [{ 'id': 1, 'field': 'name', 'heading': 'Name Initial' }, + { 'id': 2, 'field': 'email', 'heading': 'Email Initial' }]; } } diff --git a/src/modules/list-view-grid/list-view-grid.component.spec.ts b/src/modules/list-view-grid/list-view-grid.component.spec.ts index 11019dcb7..01b846895 100644 --- a/src/modules/list-view-grid/list-view-grid.component.spec.ts +++ b/src/modules/list-view-grid/list-view-grid.component.spec.ts @@ -136,22 +136,28 @@ describe('List View Grid Component', () => { })); it('should listen for the selectedColumnIdsChanged event and update the columns accordingly', - fakeAsync(() => { + (done) => { setupTest(); - flush(); - tick(110); // wait for async heading fixture.detectChanges(); + fixture.whenStable().then(() => { + fixture.detectChanges(); - component.grid.gridComponent.selectedColumnIdsChange.emit(['column1', 'column2']); - fixture.detectChanges(); - expect(element.queryAll(By.css('th.sky-grid-heading')).length).toBe(2); - expect(element.query( - By.css('th[sky-cmp-id="column1"]') - ).nativeElement.textContent.trim()).toBe('Column1'); - expect(element.query( - By.css('th[sky-cmp-id="column2"]') - ).nativeElement.textContent.trim()).toBe('Column2'); - }) + component.grid.selectedColumnIdsChange.subscribe((newColumnIds: string[]) => { + expect(newColumnIds).toEqual(['column1', 'column2']); + done(); + }); + + component.grid.gridComponent.selectedColumnIdsChange.emit(['column1', 'column2']); + fixture.detectChanges(); + expect(element.queryAll(By.css('th.sky-grid-heading')).length).toBe(2); + expect(element.query( + By.css('th[sky-cmp-id="column1"]') + ).nativeElement.textContent.trim()).toBe('Column1'); + expect(element.query( + By.css('th[sky-cmp-id="column2"]') + ).nativeElement.textContent.trim()).toBe('Column2'); + }); + } ); it('should listen for the sortFieldChange event', fakeAsync(() => { @@ -430,7 +436,7 @@ describe('List View Grid Component', () => { fixture.detectChanges(); })); - it('should handle grid columns changing', () => { + it('should handle grid columns changing to the same ids', () => { expect(element.queryAll(By.css('th.sky-grid-heading')).length).toBe(2); expect(element.query( By.css('th[sky-cmp-id="name"]')).nativeElement.textContent.trim() @@ -439,6 +445,8 @@ describe('List View Grid Component', () => { By.css('th[sky-cmp-id="email"]') ).nativeElement.textContent.trim()).toBe('Email Initial'); + spyOn(component.grid.selectedColumnIdsChange, 'emit').and.stub(); + component.changeColumns(); fixture.detectChanges(); expect(element.queryAll(By.css('th.sky-grid-heading')).length).toBe(2); @@ -448,10 +456,35 @@ describe('List View Grid Component', () => { expect(element.query( By.css('th[sky-cmp-id="email"]') ).nativeElement.textContent.trim()).toBe('Email'); + expect(component.grid.selectedColumnIdsChange.emit).not.toHaveBeenCalled(); + }); + it('should handle grid columns changing to contain a different id', (done) => { + expect(element.queryAll(By.css('th.sky-grid-heading')).length).toBe(2); + expect(element.query( + By.css('th[sky-cmp-id="name"]')).nativeElement.textContent.trim() + ).toBe('Name Initial'); + expect(element.query( + By.css('th[sky-cmp-id="email"]') + ).nativeElement.textContent.trim()).toBe('Email Initial'); + + component.grid.selectedColumnIdsChange.subscribe((newColumnIds: string[]) => { + expect(newColumnIds).toEqual(['name', 'other']); + done(); + }); + + component.changeColumnsNameAndOther(); + fixture.detectChanges(); + expect(element.queryAll(By.css('th.sky-grid-heading')).length).toBe(2); + expect(element.query( + By.css('th[sky-cmp-id="name"]')).nativeElement.textContent.trim() + ).toBe('Name'); + expect(element.query( + By.css('th[sky-cmp-id="other"]')).nativeElement.textContent.trim() + ).toBe('Other'); }); - it('should handle grid columns changing to completely different ids', () => { + it('should handle grid columns changing to contain only a different id', (done) => { expect(element.queryAll(By.css('th.sky-grid-heading')).length).toBe(2); expect(element.query( By.css('th[sky-cmp-id="name"]')).nativeElement.textContent.trim() @@ -460,13 +493,17 @@ describe('List View Grid Component', () => { By.css('th[sky-cmp-id="email"]') ).nativeElement.textContent.trim()).toBe('Email Initial'); - component.changeColumnsDifferent(); + component.grid.selectedColumnIdsChange.subscribe((newColumnIds: string[]) => { + expect(newColumnIds).toEqual(['other']); + done(); + }); + + component.changeColumnsOther(); fixture.detectChanges(); expect(element.queryAll(By.css('th.sky-grid-heading')).length).toBe(1); expect(element.query( By.css('th[sky-cmp-id="other"]')).nativeElement.textContent.trim() ).toBe('Other'); - }); }); diff --git a/src/modules/list-view-grid/list-view-grid.component.ts b/src/modules/list-view-grid/list-view-grid.component.ts index 7c65290fa..0ed25f7c7 100644 --- a/src/modules/list-view-grid/list-view-grid.component.ts +++ b/src/modules/list-view-grid/list-view-grid.component.ts @@ -7,7 +7,9 @@ import { Input, OnDestroy, QueryList, - ViewChild + ViewChild, + EventEmitter, + Output } from '@angular/core'; import { Observable } from 'rxjs/Observable'; @@ -60,7 +62,7 @@ import { ListViewDisplayedGridColumnsLoadAction } from './state/displayed-column templateUrl: './list-view-grid.component.html', providers: [ /* tslint:disable */ - { provide: ListViewComponent, useExisting: forwardRef(() => SkyListViewGridComponent)}, + { provide: ListViewComponent, useExisting: forwardRef(() => SkyListViewGridComponent) }, /* tslint:enable */ GridState, GridStateDispatcher, @@ -91,6 +93,9 @@ export class SkyListViewGridComponent @Input() public height: number | Observable; + @Output() + public selectedColumnIdsChange = new EventEmitter>(); + @ViewChild(SkyGridComponent) public gridComponent: SkyGridComponent; @@ -109,7 +114,7 @@ export class SkyListViewGridComponent private searchFunction: (data: any, searchText: string) => boolean; /* tslint:enable */ - @ContentChildren(SkyGridColumnComponent, {descendants: true}) + @ContentChildren(SkyGridColumnComponent, { descendants: true }) private columnComponents: QueryList; private ngUnsubscribe = new Subject(); @@ -210,15 +215,15 @@ export class SkyListViewGridComponent public columnIdsChanged(selectedColumnIds: Array) { this.gridState.map(s => s.columns.items) - .take(1) - .subscribe(columns => { - let displayedColumns = selectedColumnIds.map( - columnId => columns.filter(c => c.id === columnId)[0] - ); - this.gridDispatcher.next( - new ListViewDisplayedGridColumnsLoadAction(displayedColumns, true) - ); - }); + .take(1) + .subscribe(columns => { + let displayedColumns = selectedColumnIds.map( + columnId => columns.filter(c => c.id === columnId)[0] + ); + this.gridDispatcher.next( + new ListViewDisplayedGridColumnsLoadAction(displayedColumns, true) + ); + }); } public sortFieldChanged(sortField: ListSortFieldSelectorModel) { @@ -249,7 +254,7 @@ export class SkyListViewGridComponent } private handleColumnChange() { - // watch for changes in column components + // watch for changes in column components this.columnComponents.changes .takeUntil(this.ngUnsubscribe) .subscribe((columnComponents) => { @@ -276,19 +281,19 @@ export class SkyListViewGridComponent previous value of items lastUpdate ensures that we only receive the latest items. */ return this.state.map((s) => { - return s.items; + return s.items; }) - .scan((previousValue: AsyncList, newValue: AsyncList) => { - if (previousValue.lastUpdate > newValue.lastUpdate) { - return previousValue; - } else { - return newValue; - } - }) - .map((result: AsyncList) => { - return result.items; - }) - .distinctUntilChanged(); + .scan((previousValue: AsyncList, newValue: AsyncList) => { + if (previousValue.lastUpdate > newValue.lastUpdate) { + return previousValue; + } else { + return newValue; + } + }) + .map((result: AsyncList) => { + return result.items; + }) + .distinctUntilChanged(); } private getSelectedIds(): Observable> { @@ -301,18 +306,35 @@ export class SkyListViewGridComponent .map(s => s.displayedColumns) .scan( (previousValue: AsyncList, newValue: AsyncList) => { - if (previousValue.lastUpdate > newValue.lastUpdate) { - return previousValue; - } else { - return newValue; - } - }) + if (previousValue.lastUpdate > newValue.lastUpdate) { + return previousValue; + } else { + return newValue; + } + }) .map((result: AsyncList) => { /* istanbul ignore next */ /* sanity check */ return result.items.map((column: SkyGridColumnModel) => { return column.id || column.field; }); - }).distinctUntilChanged(); + }).distinctUntilChanged((previousValue: string[], newValue: string[]) => { + return this.haveColumnIdsChanged(previousValue, newValue); + }); + } + + private haveColumnIdsChanged(previousValue: string[], newValue: string[]) { + if (previousValue.length !== newValue.length) { + this.selectedColumnIdsChange.emit(newValue); + return false; + } + + for (let i = 0; i < previousValue.length; i++) { + if (previousValue[i] !== newValue[i]) { + this.selectedColumnIdsChange.emit(newValue); + return false; + } + } + return true; } }