From 0ccb19190a6376adcd2345a1a62c642a38b2f11b Mon Sep 17 00:00:00 2001 From: Jeremy Smartt Date: Sun, 7 May 2017 20:10:31 -0700 Subject: [PATCH] feat(data-table): ability to hide data table columns (closes #511) (#549) * feat: Data Table: Hide data table columns Added hidden property on columns to keep certain columns from being displayed or searched on in the filterData method. Also added unit tests for table and updated documentation. Closes #511 * fixing linting * using refresh method on datatable in demo to force change detection from data change * fix for backward compatability on filterData, fixing unit tests, backwards compatability test, and updating demo to toggles instead of buttons * changing toggle label to Hide calories on demo * updating parameter on filterdata to excludedColumns, changing label in unit test file, updating demo with latest API changes * updating documentating with correct parameter name, excludedColumns * removing undefined check for hidden parameter * removing undefined check for hidden parameter * chore(data-table): remove the undefined hidden case from unit tests * chore(data-table): refactoring unit tests to use one component also check for hidden in the hidden use case * chore(data-table): refactor and minimize unit test even more added comments in certain places too --- .../data-table/data-table.component.html | 10 +- .../data-table/data-table.component.ts | 17 ++- .../core/data-table/data-table.component.html | 2 + .../data-table/data-table.component.spec.ts | 117 +++++++++++------- .../core/data-table/data-table.component.ts | 1 + .../data-table/services/data-table.service.ts | 1 + 6 files changed, 95 insertions(+), 53 deletions(-) diff --git a/src/app/components/components/data-table/data-table.component.html b/src/app/components/components/data-table/data-table.component.html index a9d00e8ace..ed91d8e330 100644 --- a/src/app/components/components/data-table/data-table.component.html +++ b/src/app/components/components/data-table/data-table.component.html @@ -328,13 +328,14 @@

No results to display.

filter(): void { let newData: any[] = this.data; - let nonSearchAbleColumns: string[] = this.columns + let excludedColumns: string[] = this.columns .filter((column: ITdDataTableColumn) => { - return (typeof column.filter !== undefined && column.filter === false); + return ((column.filter === undefined && column.hidden === true) || + (column.filter !== undefined && column.filter === false)); }).map((column: ITdDataTableColumn) => { return column.name; }); - newData = this._dataTableService.filterData(newData, this.searchTerm, true, nonSearchAbleColumns); + newData = this._dataTableService.filterData(newData, this.searchTerm, true, excludedColumns); this.filteredTotal = newData.length; newData = this._dataTableService.sortData(newData, this.sortBy, this.sortOrder); newData = this._dataTableService.pageData(newData, this.fromRow, this.currentPage * this.pageSize); @@ -364,6 +365,9 @@

No results to display.

+ + Hide calories + Type column is searchable (search for candy) diff --git a/src/app/components/components/data-table/data-table.component.ts b/src/app/components/components/data-table/data-table.component.ts index 43091c3a0e..2802a661a7 100644 --- a/src/app/components/components/data-table/data-table.component.ts +++ b/src/app/components/components/data-table/data-table.component.ts @@ -2,7 +2,7 @@ import { Component, OnInit, HostBinding } from '@angular/core'; import { slideInDownAnimation } from '../../../app.animations'; -import { TdDataTableSortingOrder, TdDataTableService, +import { TdDataTableSortingOrder, TdDataTableService, TdDataTableComponent, ITdDataTableSortChangeEvent, ITdDataTableColumn } from '../../../../platform/core'; import { IPageChangeEvent } from '../../../../platform/core'; import { TdDialogService } from '../../../../platform/core'; @@ -90,6 +90,10 @@ export class DataTableDemoComponent implements OnInit { description: `Sets the sort order of column. Defaults to 'ASC' or TdDataTableSortingOrder.Ascending`, name: 'sortOrder', type: `['ASC' | 'DESC'] or TdDataTableSortingOrder`, + }, { + description: `Whether the column should be hidden or not. Defaults to 'false'`, + name: 'hidden', + type: `boolean`, }, { description: `When set to false this column will be excluded from searches using the filterData method.`, name: 'filter?', @@ -126,8 +130,8 @@ export class DataTableDemoComponent implements OnInit { columns: ITdDataTableColumn[] = [ { name: 'name', label: 'Dessert (100g serving)', sortable: true }, - { name: 'type', label: 'Type', filter: true }, - { name: 'calories', label: 'Calories', numeric: true, format: NUMBER_FORMAT, sortable: true}, + { name: 'type', label: 'Type', filter: true }, + { name: 'calories', label: 'Calories', numeric: true, format: NUMBER_FORMAT, sortable: true, hidden: false }, { name: 'fat', label: 'Fat (g)', numeric: true, format: DECIMAL_FORMAT, sortable: true }, { name: 'carbs', label: 'Carbs (g)', numeric: true, format: NUMBER_FORMAT }, { name: 'protein', label: 'Protein (g)', numeric: true, format: DECIMAL_FORMAT }, @@ -315,13 +319,14 @@ export class DataTableDemoComponent implements OnInit { filter(): void { let newData: any[] = this.data; - let nonSearchAbleColumns: string[] = this.columns + let excludedColumns: string[] = this.columns .filter((column: ITdDataTableColumn) => { - return (typeof column.filter !== undefined && column.filter === false); + return ((column.filter === undefined && column.hidden === true) || + (column.filter !== undefined && column.filter === false)); }).map((column: ITdDataTableColumn) => { return column.name; }); - newData = this._dataTableService.filterData(newData, this.searchTerm, true, nonSearchAbleColumns); + newData = this._dataTableService.filterData(newData, this.searchTerm, true, excludedColumns); this.filteredTotal = newData.length; newData = this._dataTableService.sortData(newData, this.sortBy, this.sortOrder); newData = this._dataTableService.pageData(newData, this.fromRow, this.currentPage * this.pageSize); diff --git a/src/platform/core/data-table/data-table.component.html b/src/platform/core/data-table/data-table.component.html index ac272290c8..0a64214776 100644 --- a/src/platform/core/data-table/data-table.component.html +++ b/src/platform/core/data-table/data-table.component.html @@ -17,6 +17,7 @@ [active]="(column.sortable || isSortable) && column === sortByColumn" [sortable]="column.sortable || isSortable" [sortOrder]="sortOrderEnum" + [hidden]="column.hidden" (sortChange)="handleSort(column)"> {{column.label}} @@ -31,6 +32,7 @@ {{column.format ? column.format(getCellValue(column, row)) : getCellValue(column, row)}} { CovalentDataTableModule, ], declarations: [ - TestFilterColumnComponent, + TdDataTableBasicComponent, TdDataTableSelectableTestComponent, ], providers: [ @@ -32,57 +33,88 @@ describe('Component: DataTable', () => { TestBed.compileComponents(); })); - it('should create the component', (done: DoneFn) => { - let fixture: ComponentFixture = TestBed.createComponent(TestFilterColumnComponent); - let component: TestFilterColumnComponent = fixture.debugElement.componentInstance; + it('should set hidden and not get search hits and set it to false and get search results', (done: DoneFn) => { + inject([TdDataTableService], (tdDataTableService: TdDataTableService) => { + let fixture: ComponentFixture = TestBed.createComponent(TdDataTableBasicComponent); + let component: TdDataTableBasicComponent = fixture.debugElement.componentInstance; + + component.columns[1].hidden = false; + // backwards compatability test + expect(tdDataTableService.filterData(component.data, '1452-2', true).length).toBe(1); - fixture.detectChanges(); - fixture.whenStable().then(() => { - expect(component).toBeTruthy(); - done(); - }); + fixture.detectChanges(); + fixture.whenStable().then(() => { + // check if there are no hidden columns + expect(fixture.debugElement.queryAll(By.directive(TdDataTableColumnComponent)) + .filter((col: DebugElement) => { + return ((col.nativeElement).attributes).hidden; + }).length).toBe(0); + + // check how many rows would return that contain Pork if no hidden columns + expect(tdDataTableService.filterData(component.data, 'Pork', true, component.columns + .filter((column: ITdDataTableColumn) => { + return column.hidden === true; + }).map((column: ITdDataTableColumn) => { + return column.name; + })).length).toBe(1); + + component.columns[1].hidden = true; + fixture.debugElement.query(By.directive(TdDataTableComponent)).componentInstance.refresh(); + fixture.detectChanges(); + fixture.whenStable().then(() => { + // check if there are hidden columns + expect(fixture.debugElement.queryAll(By.directive(TdDataTableColumnComponent)) + .filter((col: DebugElement) => { + return ((col.nativeElement).attributes).hidden; + }).length).toBe(1); + + // check how many rows would return that contain Pork if the column is hidden + expect(tdDataTableService.filterData(component.data, 'Pork', true, component.columns + .filter((column: ITdDataTableColumn) => { + return column.hidden === true; + }).map((column: ITdDataTableColumn) => { + return column.name; + })).length).toBe(0); + done(); + }); + }); + })(); }); it('should set filter and not get search hits and set it to false and get search results', (done: DoneFn) => { inject([TdDataTableService], (tdDataTableService: TdDataTableService) => { - let fixture: ComponentFixture = TestBed.createComponent(TestFilterColumnComponent); - let component: TestFilterColumnComponent = fixture.debugElement.componentInstance; + let fixture: ComponentFixture = TestBed.createComponent(TdDataTableBasicComponent); + let component: TdDataTableBasicComponent = fixture.debugElement.componentInstance; + + component.columns[1].filter = false; fixture.detectChanges(); fixture.whenStable().then(() => { - let columns: ITdDataTableColumn[] = fixture.debugElement.query(By.directive(TdDataTableComponent)).componentInstance.columns; - expect(columns[1].filter).toBe(false); + expect(component.columns[1].filter).toBe(false); - let newData: any[] = component.data; // backwards compatability test - newData = tdDataTableService.filterData(newData, '1452-2', true); - fixture.detectChanges(); - fixture.whenStable().then(() => { - expect(newData.length).toBe(1); + expect(tdDataTableService.filterData(component.data, '1452-2', true).length).toBe(1); - let nonSearchAbleColumns: string[] = component.columns + // check how many rows would return that contain Pork if the second column has filter = false + expect(tdDataTableService.filterData(component.data, 'Pork', true, component.columns .filter((column: ITdDataTableColumn) => { return (typeof column.filter !== undefined && column.filter === false); }).map((column: ITdDataTableColumn) => { return column.name; - }); - newData = component.data; - newData = tdDataTableService.filterData(newData, 'Pork', true, nonSearchAbleColumns); - fixture.detectChanges(); - fixture.whenStable().then(() => { - expect(newData.length).toBe(0); - - fixture.detectChanges(); - fixture.whenStable().then(() => { - newData = component.data; - newData = tdDataTableService.filterData(newData, 'Pork', true, []); - fixture.detectChanges(); - fixture.whenStable().then(() => { - expect(newData.length).toBe(1); - done(); - }); - }); - }); + })).length).toBe(0); + + // set the second column as filtered + component.columns[1].filter = true; + fixture.detectChanges(); + fixture.whenStable().then(() => { + // check how many rows would return that contain Pork if the seconds column has filter = true + expect(tdDataTableService.filterData(component.data, 'Pork', true, component.columns + .filter((column: ITdDataTableColumn) => { + return (typeof column.filter !== undefined && column.filter === false); + }).map((column: ITdDataTableColumn) => { + return column.name; + })).length).toBe(1); + done(); }); }); })(); @@ -109,23 +141,20 @@ describe('Component: DataTable', () => { @Component({ template: ` `, }) -class TestFilterColumnComponent { +class TdDataTableBasicComponent { data: any[] = [ { sku: '1452-2', item: 'Pork Chops', price: 32.11 }, { sku: '1421-0', item: 'Prime Rib', price: 41.15 }, ]; columns: ITdDataTableColumn[] = [ - { name: 'sku', label: 'SKU #', tooltip: 'Stock Keeping Unit' }, - { name: 'item', label: 'Item name', filter: false }, + { name: 'sku', label: 'SKU #' }, + { name: 'item', label: 'Item name' }, { name: 'price', label: 'Price (US$)', numeric: true }, ]; - - filteredData: any[] = this.data; } @Component({ diff --git a/src/platform/core/data-table/data-table.component.ts b/src/platform/core/data-table/data-table.component.ts index efd4dc9442..addfa8b481 100644 --- a/src/platform/core/data-table/data-table.component.ts +++ b/src/platform/core/data-table/data-table.component.ts @@ -28,6 +28,7 @@ export interface ITdDataTableColumn { format?: (value: any) => any; nested?: boolean; sortable?: boolean; + hidden?: boolean; filter?: boolean; } diff --git a/src/platform/core/data-table/services/data-table.service.ts b/src/platform/core/data-table/services/data-table.service.ts index f4b1f3ecc4..906b2155c3 100644 --- a/src/platform/core/data-table/services/data-table.service.ts +++ b/src/platform/core/data-table/services/data-table.service.ts @@ -10,6 +10,7 @@ export class TdDataTableService { * - data: any[] * - searchTerm: string * - ignoreCase: boolean = false + * - excludedColumns: string[] = [] * * Searches [data] parameter for [searchTerm] matches and returns a new array with them. */