diff --git a/src/material/table/table-data-source.spec.ts b/src/material/table/table-data-source.spec.ts index b46c14d2ad08..feaf4a846e2a 100644 --- a/src/material/table/table-data-source.spec.ts +++ b/src/material/table/table-data-source.spec.ts @@ -1,11 +1,17 @@ import {MatTableDataSource} from './table-data-source'; -import {ComponentFixture, TestBed} from '@angular/core/testing'; +import {ComponentFixture, fakeAsync, TestBed, tick} from '@angular/core/testing'; import {MatSort, MatSortModule} from '@angular/material/sort'; import {Component, ViewChild} from '@angular/core'; +declare global { + interface Window { + ngDevMode?: object | null; + } +} + describe('MatTableDataSource', () => { describe('sort', () => { - let dataSource: MatTableDataSource; + let dataSource: MatTableDataSource<{'prop': string | number}>; let fixture: ComponentFixture; let sort: MatSort; @@ -18,7 +24,7 @@ describe('MatTableDataSource', () => { }); /** Test the data source's `sortData` function. */ - function testSortWithValues(values: any[]) { + function testSortWithValues(values: (string | number)[]) { // The data source and MatSort expect the list to contain objects with values, where // the sort should be performed over a particular key. // Map the values into an array of objects where each value is keyed by "prop" @@ -73,13 +79,45 @@ describe('MatTableDataSource', () => { }); it('should update filteredData even if the data source is disconnected', () => { - dataSource.data = [1, 2, 3, 4, 5]; - expect(dataSource.filteredData).toEqual([1, 2, 3, 4, 5]); + dataSource.data = [{'prop': 1}, {'prop': 2}, {'prop': 3}]; + expect(dataSource.filteredData).toEqual([{'prop': 1}, {'prop': 2}, {'prop': 3}]); dataSource.disconnect(); - dataSource.data = [5, 4, 3, 2, 1]; - expect(dataSource.filteredData).toEqual([5, 4, 3, 2, 1]); + dataSource.data = [{'prop': 3}, {'prop': 2}, {'prop': 1}]; + expect(dataSource.filteredData).toEqual([{'prop': 3}, {'prop': 2}, {'prop': 1}]); }); + + it('should filter data', () => { + dataSource.data = [{'prop': 1}, {'prop': 'foo'}, {'prop': 'banana'}]; + dataSource.filter = 'b'; + expect(dataSource.filteredData).toEqual([{'prop': 'banana'}]); + }); + + it('does not warn in non-dev mode when filtering non-object data', fakeAsync(() => { + const warnSpy = spyOn(console, 'warn'); + window.ngDevMode = null; + dataSource.data = [1, 2, 3, 4, 5] as unknown as {'prop': number}[]; + + dataSource.filter = '1'; + tick(); + + expect(warnSpy).not.toHaveBeenCalled(); + expect(dataSource.filteredData).toEqual([]); + })); + + it('displays the warning in dev mode when filtering non-object data', fakeAsync(() => { + const warnSpy = spyOn(console, 'warn'); + window.ngDevMode = {}; + dataSource.data = [1, 2, 3, 4, 5] as unknown as {'prop': number}[]; + + dataSource.filter = '1'; + tick(); + + expect(warnSpy).toHaveBeenCalledWith( + jasmine.stringContaining('requires data to be a non-null object'), + ); + expect(dataSource.filteredData).toEqual([]); + })); }); }); diff --git a/src/material/table/table-data-source.ts b/src/material/table/table-data-source.ts index 984b4d9fef3d..96b944a6d555 100644 --- a/src/material/table/table-data-source.ts +++ b/src/material/table/table-data-source.ts @@ -233,6 +233,15 @@ export class MatTableDataSource< * @returns Whether the filter matches against the data */ filterPredicate: (data: T, filter: string) => boolean = (data: T, filter: string): boolean => { + if ( + (typeof ngDevMode === 'undefined' || ngDevMode) && + (typeof data !== 'object' || data === null) + ) { + console.warn( + 'Default implementation of filterPredicate requires data to be a non-null object.', + ); + } + // Transform the filter by converting it to lowercase and removing whitespace. const transformedFilter = filter.trim().toLowerCase(); // Loops over the values in the array and returns true if any of them match the filter string