Skip to content

Commit

Permalink
feat(data-table): ability to exclude columns when filtering data (closes
Browse files Browse the repository at this point in the history
 #513) (#548)

* feat: Data Table: Searchable and unsearchable columns
Added notsearchable property on columns to keep certain columns from being searched on in the filterData method. Also added unit tests for table and updated documentation.

Closes #513

* fixing linting

* updating notsearchable to filter

* defaulting filter on demo to true

* update(docs): data-table demo toggles

* update(docs): fix filter

* Updating to pass in an array of strings of column names to exclude from the search rather than the full columns config object. This will allow the columns by which you are searching on to be more easily changed on the fly

* fix for backward compatability on filterData, fixing unit tests, backwards compatability test

* updating parameter on filterdata to excludedColumns, changing label in unit test file, updating demo with latest API changes
  • Loading branch information
jeremysmartt authored and emoralesb05 committed May 5, 2017
1 parent bec8a3a commit 11c3d15
Show file tree
Hide file tree
Showing 5 changed files with 166 additions and 38 deletions.
42 changes: 28 additions & 14 deletions src/app/components/components/data-table/data-table.component.html
Original file line number Diff line number Diff line change
Expand Up @@ -289,7 +289,7 @@ <h3>No results to display.</h3>
columns: ITdDataTableColumn[] = [
{ name: 'sku', label: 'SKU #', tooltip: 'Stock Keeping Unit' },
{ name: 'item', label: 'Item name' },
{ name: 'price', label: 'Price (US$)', numeric: true, format: v => v.toFixed(2) },
{ name: 'price', label: 'Price (US$)', numeric: true, format: v => v.toFixed(2), filter: true },
];

filteredData: any[] = this.data;
Expand Down Expand Up @@ -328,7 +328,13 @@ <h3>No results to display.</h3>

filter(): void {
let newData: any[] = this.data;
newData = this._dataTableService.filterData(newData, this.searchTerm, true);
let nonSearchAbleColumns: string[] = this.columns
.filter((column: ITdDataTableColumn) => {
return (typeof column.filter !== undefined && column.filter === false);
}).map((column: ITdDataTableColumn) => {
return column.name;
});
newData = this._dataTableService.filterData(newData, this.searchTerm, true, nonSearchAbleColumns);
this.filteredTotal = newData.length;
newData = this._dataTableService.sortData(newData, this.sortBy, this.sortOrder);
newData = this._dataTableService.pageData(newData, this.fromRow, this.currentPage * this.pageSize);
Expand All @@ -342,18 +348,26 @@ <h3>No results to display.</h3>
</md-card-content>
<md-divider></md-divider>
<md-card-actions>
<button md-button color="primary" (click)="toggleSelectable()" class="text-upper">
Toggle Selectable
({{selectable ? 'ON' : 'OFF'}})
</button>
<button md-button color="primary" (click)="toggleMultiple()" [disabled]="!selectable" class="text-upper">
Toggle Multiple
({{multiple ? 'ON' : 'OFF'}})
</button>
<button md-button color="primary" (click)="toggleTooltips()" class="text-upper">
Toggle Tooltip
({{areTooltipsOn() ? 'ON' : 'OFF'}})
</button>
<div class="pad-sm">
<md-slide-toggle color="accent" [(ngModel)]="selectable">
Selectable rows
</md-slide-toggle>
<md-slide-toggle class="push-left" color="accent" [(ngModel)]="multiple" [disabled]="!selectable">
Select multiple rows
</md-slide-toggle>
</div>
<md-divider></md-divider>
<div class="pad-sm">
<md-slide-toggle color="accent" (change)="toggleTooltips()">
Tooltips on column headers
</md-slide-toggle>
</div>
<md-divider></md-divider>
<div class="pad-sm">
<md-slide-toggle color="accent" [(ngModel)]="columns[1].filter">
Type column is searchable (search for <code>candy</code>)
</md-slide-toggle>
</div>
</md-card-actions>
</md-card>
<md-card>
Expand Down
38 changes: 19 additions & 19 deletions src/app/components/components/data-table/data-table.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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: `When set to false this column will be excluded from searches using the filterData method.`,
name: 'filter?',
type: 'boolean',
}, {
description: `Sets column to active state when 'true'. Defaults to 'false'`,
name: 'active',
Expand All @@ -106,9 +110,10 @@ export class DataTableDemoComponent implements OnInit {
}];

serviceAttrs: Object[] = [{
description: `Searches [data] parameter for [searchTerm] matches and returns a new array with them.`,
description: `Searches [data] parameter for [searchTerm] matches and returns a new array with them.
Any column names passed in with [nonSearchAbleColumns] will be excluded in the search.`,
name: 'filterData',
type: `function(data: any[], searchTerm: string, ignoreCase: boolean)`,
type: `function(data: any[], searchTerm: string, ignoreCase: boolean, nonSearchAbleColumns: string[])`,
}, {
description: `Sorts [data] parameter by [sortBy] and [sortOrder] and returns the sorted data.`,
name: 'sortData',
Expand All @@ -121,8 +126,8 @@ export class DataTableDemoComponent implements OnInit {

columns: ITdDataTableColumn[] = [
{ name: 'name', label: 'Dessert (100g serving)', sortable: true },
{ name: 'type', label: 'Type' },
{ 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},
{ 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 },
Expand Down Expand Up @@ -257,8 +262,9 @@ export class DataTableDemoComponent implements OnInit {
},
];
basicData: any[] = this.data.slice(0, 4);
selectable: boolean = false;
multiple: boolean = false;
selectable: boolean = true;
multiple: boolean = true;
filterColumn: boolean = true;

filteredData: any[] = this.data;
filteredTotal: number = this.data.length;
Expand Down Expand Up @@ -309,25 +315,19 @@ export class DataTableDemoComponent implements OnInit {

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

toggleSelectable(): void {
this.selectable = !this.selectable;
}

toggleMultiple(): void {
this.multiple = !this.multiple;
}

areTooltipsOn(): boolean {
return this.columns[0].hasOwnProperty('tooltip');
}

toggleTooltips(): void {
if (this.columns[0].tooltip) {
this.columns.forEach((c: any) => delete c.tooltip);
Expand Down
111 changes: 111 additions & 0 deletions src/platform/core/data-table/data-table.component.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
import {
TestBed,
inject,
async,
ComponentFixture,
} from '@angular/core/testing';
import 'hammerjs';
import { Component } from '@angular/core';
import { By } from '@angular/platform-browser';
import { TdDataTableComponent, ITdDataTableColumn } from './data-table.component';
import { TdDataTableService } from './services/data-table.service';
import { CovalentDataTableModule } from './data-table.module';
import { NgModule, DebugElement } from '@angular/core';
import { BrowserAnimationsModule } from '@angular/platform-browser/animations';

describe('Component: TdDataTableComponent', () => {

beforeEach(async(() => {
TestBed.configureTestingModule({
imports: [
BrowserAnimationsModule,
CovalentDataTableModule,
],
declarations: [
TestFilterColumnComponent,
],
providers: [
TdDataTableService,
],
});
TestBed.compileComponents();
}));

it('should create the component', (done: DoneFn) => {
let fixture: ComponentFixture<any> = TestBed.createComponent(TestFilterColumnComponent);
let component: TestFilterColumnComponent = fixture.debugElement.componentInstance;

fixture.detectChanges();
fixture.whenStable().then(() => {
expect(component).toBeTruthy();
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<any> = TestBed.createComponent(TestFilterColumnComponent);
let component: TestFilterColumnComponent = fixture.debugElement.componentInstance;

fixture.detectChanges();
fixture.whenStable().then(() => {
let columns: ITdDataTableColumn[] = fixture.debugElement.query(By.directive(TdDataTableComponent)).componentInstance.columns;
expect(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);

let nonSearchAbleColumns: string[] = 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();
});
});
});
});
});
})();
});
});

@Component({
template: `
<td-data-table
#dataTable
[data]="filteredData"
[columns]="columns">
</td-data-table>`,
})
class TestFilterColumnComponent {
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: 'price', label: 'Price (US$)', numeric: true },
];

filteredData: any[] = this.data;
}
1 change: 1 addition & 0 deletions src/platform/core/data-table/data-table.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ export interface ITdDataTableColumn {
format?: (value: any) => any;
nested?: boolean;
sortable?: boolean;
filter?: boolean;
}

export interface ITdDataTableSelectEvent {
Expand Down
12 changes: 7 additions & 5 deletions src/platform/core/data-table/services/data-table.service.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { Injectable } from '@angular/core';

import { TdDataTableSortingOrder } from '../data-table.component';
import { TdDataTableSortingOrder, ITdDataTableColumn } from '../data-table.component';

@Injectable()
export class TdDataTableService {
Expand All @@ -13,14 +13,16 @@ export class TdDataTableService {
*
* Searches [data] parameter for [searchTerm] matches and returns a new array with them.
*/
filterData(data: any[], searchTerm: string, ignoreCase: boolean = false): any[] {
filterData(data: any[], searchTerm: string, ignoreCase: boolean = false, excludedColumns?: string[]): any[] {
let filter: string = searchTerm ? (ignoreCase ? searchTerm.toLowerCase() : searchTerm) : '';
if (filter) {
data = data.filter((item: any) => {
const res: any = Object.keys(item).find((key: string) => {
const preItemValue: string = ('' + item[key]);
const itemValue: string = ignoreCase ? preItemValue.toLowerCase() : preItemValue;
return itemValue.indexOf(filter) > -1;
if (!excludedColumns || excludedColumns.indexOf(key) === -1) {
const preItemValue: string = ('' + item[key]);
const itemValue: string = ignoreCase ? preItemValue.toLowerCase() : preItemValue;
return itemValue.indexOf(filter) > -1;
}
});
return !(typeof res === 'undefined');
});
Expand Down

0 comments on commit 11c3d15

Please sign in to comment.