diff --git a/package.json b/package.json
index a1094a0..3d744b9 100644
--- a/package.json
+++ b/package.json
@@ -26,6 +26,7 @@
"@angular/common": ">=4.3.6",
"@angular/core": ">=4.3.6",
"@angular/forms": ">=4.3.6",
+ "@skyux/core": "^3.5.1",
"@skyux/i18n": "^3.5.0",
"@skyux/list-builder-common": "^3.0.0",
"@skyux/forms": "^3.0.0",
diff --git a/src/app/public/modules/grid/fixtures/grid-async.component.fixture.html b/src/app/public/modules/grid/fixtures/grid-async.component.fixture.html
index c8c8168..6d8923f 100644
--- a/src/app/public/modules/grid/fixtures/grid-async.component.fixture.html
+++ b/src/app/public/modules/grid/fixtures/grid-async.component.fixture.html
@@ -5,4 +5,9 @@
[heading]="asyncHeading | async"
[description]="asyncDescription | async">
+
+
diff --git a/src/app/public/modules/grid/fixtures/grid-empty.component.fixture.html b/src/app/public/modules/grid/fixtures/grid-empty.component.fixture.html
index a129d3b..d6977ad 100644
--- a/src/app/public/modules/grid/fixtures/grid-empty.component.fixture.html
+++ b/src/app/public/modules/grid/fixtures/grid-empty.component.fixture.html
@@ -1,3 +1,9 @@
-
+
{{value}}
diff --git a/src/app/public/modules/grid/fixtures/grid-empty.component.fixture.ts b/src/app/public/modules/grid/fixtures/grid-empty.component.fixture.ts
index 86e9577..34f00fa 100644
--- a/src/app/public/modules/grid/fixtures/grid-empty.component.fixture.ts
+++ b/src/app/public/modules/grid/fixtures/grid-empty.component.fixture.ts
@@ -21,6 +21,8 @@ export class GridEmptyTestComponent {
public template: TemplateRef;
public columns: Array;
+ public selectedColumnIds: string[];
+ public settingsKey: string;
public data: any[] = [
{
diff --git a/src/app/public/modules/grid/fixtures/grid-fixtures.module.ts b/src/app/public/modules/grid/fixtures/grid-fixtures.module.ts
index 2f53e3a..5ed8992 100644
--- a/src/app/public/modules/grid/fixtures/grid-fixtures.module.ts
+++ b/src/app/public/modules/grid/fixtures/grid-fixtures.module.ts
@@ -7,12 +7,17 @@ import {
} from '@angular/common';
import {
+ SkyUIConfigService,
SkyWindowRefService
} from '@skyux/core';
+import {
+ Observable
+} from 'rxjs/Observable';
+
import {
SkyGridModule
-} from '../';
+} from '../grid.module';
import {
GridTestComponent
@@ -47,7 +52,16 @@ import {
SkyGridModule
],
providers: [
- SkyWindowRefService
+ SkyWindowRefService,
+ {
+ provide: SkyUIConfigService,
+ useValue: {
+ getConfig: () => Observable.of({
+ selectedColumnIds: []
+ }),
+ setConfig: () => Observable.of({})
+ }
+ }
],
exports: [
GridTestComponent,
diff --git a/src/app/public/modules/grid/fixtures/grid.component.fixture.html b/src/app/public/modules/grid/fixtures/grid.component.fixture.html
index 2c84eb3..e5d22db 100644
--- a/src/app/public/modules/grid/fixtures/grid.component.fixture.html
+++ b/src/app/public/modules/grid/fixtures/grid.component.fixture.html
@@ -10,6 +10,7 @@
[messageStream]="gridController"
[multiselectRowId]="multiselectRowId"
[rowHighlightedId]="rowHighlightedId"
+ [settingsKey]="settingsKey"
[sortField]="sortField"
(sortFieldChange)="onSort($event)"
(columnWidthChange)="onResize($event)"
diff --git a/src/app/public/modules/grid/fixtures/grid.component.fixture.ts b/src/app/public/modules/grid/fixtures/grid.component.fixture.ts
index 184f6a3..a827aa7 100644
--- a/src/app/public/modules/grid/fixtures/grid.component.fixture.ts
+++ b/src/app/public/modules/grid/fixtures/grid.component.fixture.ts
@@ -53,6 +53,7 @@ export class GridTestComponent {
public selectedRowsChange: SkyGridSelectedRowsModelChange;
public gridController = new Subject();
public rowHighlightedId: string;
+ public settingsKey: string;
public selectedColumnIds: string[] = [
'column1',
diff --git a/src/app/public/modules/grid/grid.component.spec.ts b/src/app/public/modules/grid/grid.component.spec.ts
index eb7dd5d..b598962 100644
--- a/src/app/public/modules/grid/grid.component.spec.ts
+++ b/src/app/public/modules/grid/grid.component.spec.ts
@@ -19,10 +19,18 @@ import {
tick
} from '@angular/core/testing';
+import {
+ SkyUIConfigService
+} from '@skyux/core';
+
import {
DragulaService
} from 'ng2-dragula/ng2-dragula';
+import {
+ Observable
+} from 'rxjs/Observable';
+
const moment = require('moment');
import {
@@ -54,7 +62,6 @@ import {
} from './fixtures/mock-dragula.service';
import {
- SkyGridModule,
SkyGridComponent,
SkyGridColumnModel
} from './';
@@ -207,8 +214,7 @@ describe('Grid Component', () => {
beforeEach(async(() => {
TestBed.configureTestingModule({
imports: [
- GridFixturesModule,
- SkyGridModule
+ GridFixturesModule
]
});
}));
@@ -590,7 +596,7 @@ describe('Grid Component', () => {
});
});
- describe('Resiazable columns', () => {
+ describe('Resizeable columns', () => {
it('should not resize if user does not use resize handle', fakeAsync(() => {
// Get initial baseline for comparison.
@@ -825,8 +831,7 @@ describe('Grid Component', () => {
beforeEach(() => {
TestBed.configureTestingModule({
imports: [
- GridFixturesModule,
- SkyGridModule
+ GridFixturesModule
]
});
});
@@ -946,8 +951,7 @@ describe('Grid Component', () => {
beforeEach(() => {
TestBed.configureTestingModule({
imports: [
- GridFixturesModule,
- SkyGridModule
+ GridFixturesModule
]
});
});
@@ -1266,8 +1270,7 @@ describe('Grid Component', () => {
beforeEach(() => {
TestBed.configureTestingModule({
imports: [
- GridFixturesModule,
- SkyGridModule
+ GridFixturesModule
]
});
});
@@ -1323,8 +1326,7 @@ describe('Grid Component', () => {
TestBed.configureTestingModule({
imports: [
- GridFixturesModule,
- SkyGridModule
+ GridFixturesModule
]
});
@@ -1552,8 +1554,7 @@ describe('Grid Component', () => {
beforeEach(async(() => {
TestBed.configureTestingModule({
imports: [
- GridFixturesModule,
- SkyGridModule
+ GridFixturesModule
]
});
}));
@@ -1639,8 +1640,7 @@ describe('Grid Component', () => {
TestBed.configureTestingModule({
imports: [
- GridFixturesModule,
- SkyGridModule
+ GridFixturesModule
]
});
@@ -1674,8 +1674,7 @@ describe('Grid Component', () => {
beforeEach(async(() => {
TestBed.configureTestingModule({
imports: [
- GridFixturesModule,
- SkyGridModule
+ GridFixturesModule
]
}).compileComponents();
}));
@@ -1685,20 +1684,23 @@ describe('Grid Component', () => {
element = fixture.debugElement as DebugElement;
});
- it('should handle async column headings', fakeAsync(() => {
- fixture.detectChanges();
-
- expect(getColumnHeader('column1', element).nativeElement.textContent.trim()).toBe('');
+ function verifyColumnHeaders(id: string) {
+ expect(getColumnHeader(id, element).nativeElement.textContent.trim()).toBe('');
tick(110); // wait for setTimeout
fixture.detectChanges();
tick();
- expect(getColumnHeader('column1', element).nativeElement.textContent.trim()).toBe('Column1');
- }));
+ expect(getColumnHeader(id, element).nativeElement.textContent.trim()).toBe('Column1');
+ }
it('should handle async column headings', fakeAsync(() => {
fixture.detectChanges();
+ verifyColumnHeaders('column1');
+ }));
+
+ it('should handle async column descriptions', fakeAsync(() => {
+ fixture.detectChanges();
let col1 = fixture.componentInstance.grid.columns.find(col => col.id === 'column1');
expect(col1.description).toBe('');
@@ -1709,5 +1711,117 @@ describe('Grid Component', () => {
expect(col1.description).toBe('Column1 Description');
}));
+
+ it('should support the item `field` property if `id` not provided', fakeAsync(() => {
+ fixture.detectChanges();
+
+ verifyColumnHeaders('column2');
+ }));
+ });
+
+ describe('UI config', () => {
+ let fixture: ComponentFixture;
+ let component: GridEmptyTestComponent;
+ let uiConfigService: SkyUIConfigService;
+
+ beforeEach(() => {
+ TestBed.configureTestingModule({
+ imports: [
+ GridFixturesModule
+ ]
+ });
+
+ fixture = TestBed.createComponent(GridEmptyTestComponent);
+ component = fixture.componentInstance;
+ uiConfigService = TestBed.get(SkyUIConfigService);
+ });
+
+ it('should call the UI config service when selected columns change', () => {
+ component.columns = [
+ new SkyGridColumnModel(component.template, {
+ id: 'column1',
+ heading: 'Column 1'
+ }),
+ new SkyGridColumnModel(component.template, {
+ id: 'column2',
+ heading: 'Column 2'
+ })
+ ];
+
+ fixture.detectChanges();
+
+ const spy = spyOn(uiConfigService, 'setConfig').and.callThrough();
+
+ component.settingsKey = 'foobar';
+ component.selectedColumnIds = ['column1', 'column2'];
+ fixture.detectChanges();
+
+ expect(spy.calls.count()).toEqual(1);
+
+ spy.calls.reset();
+ component.selectedColumnIds = ['column2', 'column1'];
+ fixture.detectChanges();
+
+ expect(spy.calls.count()).toEqual(1);
+ });
+
+ it('should fetch UI config on init', () => {
+ const spy = spyOn(uiConfigService, 'getConfig').and.callThrough();
+
+ component.settingsKey = 'foobar';
+ fixture.detectChanges();
+
+ expect(spy).toHaveBeenCalledWith('foobar');
+ });
+
+ it('should handle errors when setting config', () => {
+ const spy = spyOn(console, 'warn');
+
+ spyOn(uiConfigService, 'setConfig').and.callFake(() => {
+ return Observable.throw(new Error());
+ });
+
+ component.columns = [
+ new SkyGridColumnModel(component.template, {
+ id: 'column1',
+ heading: 'Column 1'
+ }),
+ new SkyGridColumnModel(component.template, {
+ id: 'column2',
+ heading: 'Column 2'
+ })
+ ];
+
+ fixture.detectChanges();
+
+ component.settingsKey = 'foobar';
+ component.selectedColumnIds = ['column1', 'column2'];
+ fixture.detectChanges();
+
+ expect(spy).toHaveBeenCalledWith('Could not save grid settings.');
+ });
+
+ it('should suppress errors when getting config', () => {
+ spyOn(uiConfigService, 'getConfig').and.callFake(() => {
+ return Observable.throw(new Error());
+ });
+
+ const spy = spyOn(fixture.componentInstance.grid as any, 'initColumns').and.callThrough();
+
+ component.columns = [
+ new SkyGridColumnModel(component.template, {
+ id: 'column1',
+ heading: 'Column 1'
+ })
+ ];
+
+ component.settingsKey = 'foobar';
+ fixture.detectChanges();
+
+ fixture.whenStable().then(() => {
+ fixture.detectChanges();
+ expect(spy).toHaveBeenCalled();
+ });
+ });
});
});
diff --git a/src/app/public/modules/grid/grid.component.ts b/src/app/public/modules/grid/grid.component.ts
index 97cd56e..9a5ec92 100644
--- a/src/app/public/modules/grid/grid.component.ts
+++ b/src/app/public/modules/grid/grid.component.ts
@@ -17,6 +17,10 @@ import {
OnInit
} from '@angular/core';
+import {
+ SkyUIConfigService
+} from '@skyux/core';
+
import {
Observable
} from 'rxjs/Observable';
@@ -80,6 +84,10 @@ import {
SkyGridMessageType
} from './types';
+import {
+ SkyGridUIConfig
+} from './types/grid-ui-config';
+
import '../../polyfills';
let nextId = 0;
@@ -134,6 +142,9 @@ export class SkyGridComponent implements OnInit, AfterContentInit, OnChanges, On
@Input()
public rowHighlightedId: string;
+ @Input()
+ public settingsKey: string;
+
@Output()
public selectedColumnIdsChange = new EventEmitter>();
@@ -184,7 +195,8 @@ export class SkyGridComponent implements OnInit, AfterContentInit, OnChanges, On
private dragulaService: DragulaService,
private ref: ChangeDetectorRef,
private gridAdapter: SkyGridAdapterService,
- private skyWindow: SkyWindowRefService
+ private skyWindow: SkyWindowRefService,
+ private uiConfigService: SkyUIConfigService
) {
this.displayedColumns = new Array();
this.items = new Array();
@@ -203,38 +215,15 @@ export class SkyGridComponent implements OnInit, AfterContentInit, OnChanges, On
}
public ngAfterContentInit() {
- if (this.columnComponents.length !== 0 || this.columns !== undefined) {
- /* istanbul ignore else */
- /* sanity check */
- if (this.columnComponents.length > 0) {
- this.getColumnsFromComponent();
- }
-
- this.transformData();
- this.setDisplayedColumns(true);
+ if (this.settingsKey) {
+ this.applyUserConfig().then(() => {
+ this.initColumns();
+ });
+ } else {
+ this.initColumns();
}
- // 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.headingModelChanges
- .subscribe((change: SkyGridColumnHeadingModelChange) => {
- this.updateColumnHeading(change);
- })
- );
- this.subscriptions.push(
- comp.descriptionModelChanges
- .subscribe((change: SkyGridColumnDescriptionModelChange) => {
- this.updateColumnDescription(change);
- })
- );
- });
-
+ // Setup column drag-and-drop.
this.gridAdapter.initializeDragAndDrop(
this.dragulaService,
(selectedColumnIds: Array) => {
@@ -243,23 +232,36 @@ export class SkyGridComponent implements OnInit, AfterContentInit, OnChanges, On
);
}
- // Do an ngOnChanges where changes to selectedColumnIds and data are watched
public ngOnChanges(changes: SimpleChanges) {
- if (changes['columns'] && this.columns) {
- this.setDisplayedColumns(true);
- } else if (changes['selectedColumnIds'] && this.columns) {
+ if (
+ changes.selectedColumnIds &&
+ changes.selectedColumnIds.firstChange === false
+ ) {
this.setDisplayedColumns();
- if (changes['selectedColumnIds'].previousValue !== changes['selectedColumnIds'].currentValue) {
+
+ this.setUserConfig({
+ selectedColumnIds: this.selectedColumnIds
+ });
+
+ /* istanbul ignore else */
+ if (
+ changes.selectedColumnIds.previousValue !==
+ changes.selectedColumnIds.currentValue
+ ) {
this.selectedColumnIdsChange.emit(this.selectedColumnIds);
this.resetTableWidth();
}
}
- if (changes['data'] && this.data) {
+ if (changes.columns && this.columns) {
+ this.setDisplayedColumns(true);
+ }
+
+ if (changes.data && this.data) {
this.transformData();
}
- if (changes['sortField']) {
+ if (changes.sortField) {
this.setSortHeaders();
}
}
@@ -493,6 +495,7 @@ export class SkyGridComponent implements OnInit, AfterContentInit, OnChanges, On
}
public onRowClick(event: any, selectedItem: ListItemModel) {
+ /* istanbul ignore else */
if (this.enableMultiselect) {
if (event.target === event.currentTarget || !this.isInteractiveElement(event)) {
selectedItem.isSelected = !selectedItem.isSelected;
@@ -548,6 +551,10 @@ export class SkyGridComponent implements OnInit, AfterContentInit, OnChanges, On
columnId => this.columns.filter(column => column.id === columnId)[0]
);
+ this.setUserConfig({
+ selectedColumnIds: this.selectedColumnIds
+ });
+
// mark for check because we are using ChangeDetectionStrategy.onPush
this.ref.markForCheck();
}
@@ -772,4 +779,80 @@ export class SkyGridComponent implements OnInit, AfterContentInit, OnChanges, On
summary`;
return event.target.closest(interactiveElSelectors);
}
+
+ private applyUserConfig(): Promise {
+ return new Promise((resolve) => {
+ this.uiConfigService.getConfig(this.settingsKey)
+ .take(1)
+ .subscribe((config) => {
+ /* istanbul ignore else */
+ if (config && config.selectedColumnIds) {
+ this.selectedColumnIds = config.selectedColumnIds;
+ this.ref.markForCheck();
+ }
+
+ resolve();
+ }, () => {
+ resolve();
+ });
+ });
+ }
+
+ private setUserConfig(config: SkyGridUIConfig): void {
+ if (!this.settingsKey) {
+ return;
+ }
+
+ this.uiConfigService.setConfig(
+ this.settingsKey,
+ config
+ )
+ .takeUntil(this.ngUnsubscribe)
+ .subscribe(
+ () => { },
+ (err) => {
+ console.warn('Could not save grid settings.');
+ console.warn(err);
+ }
+ );
+ }
+
+ private initColumns(): void {
+ /* istanbul ignore else */
+ if (
+ this.columnComponents.length !== 0 ||
+ this.columns !== undefined
+ ) {
+ /* istanbul ignore else */
+ /* sanity check */
+ if (this.columnComponents.length > 0) {
+ this.getColumnsFromComponent();
+ }
+
+ this.transformData();
+ 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.headingModelChanges
+ .subscribe((change: SkyGridColumnHeadingModelChange) => {
+ this.updateColumnHeading(change);
+ })
+ );
+ this.subscriptions.push(
+ comp.descriptionModelChanges
+ .subscribe((change: SkyGridColumnDescriptionModelChange) => {
+ this.updateColumnDescription(change);
+ })
+ );
+ });
+ }
}
diff --git a/src/app/public/modules/grid/grid.module.ts b/src/app/public/modules/grid/grid.module.ts
index 2990885..0b94954 100644
--- a/src/app/public/modules/grid/grid.module.ts
+++ b/src/app/public/modules/grid/grid.module.ts
@@ -14,6 +14,10 @@ import {
DragulaModule
} from 'ng2-dragula/ng2-dragula';
+import {
+ SkyUIConfigService
+} from '@skyux/core';
+
import {
SkyCheckboxModule
} from '@skyux/forms';
@@ -58,6 +62,9 @@ import {
SkyGridComponent,
SkyGridColumnComponent,
SkyGridCellComponent
+ ],
+ providers: [
+ SkyUIConfigService
]
})
export class SkyGridModule {
diff --git a/src/app/public/modules/grid/types/grid-ui-config.ts b/src/app/public/modules/grid/types/grid-ui-config.ts
new file mode 100644
index 0000000..b58aa39
--- /dev/null
+++ b/src/app/public/modules/grid/types/grid-ui-config.ts
@@ -0,0 +1,3 @@
+export interface SkyGridUIConfig {
+ selectedColumnIds?: string[];
+}