@@ -11,7 +11,8 @@ import {
1111 getTableMissingMatchingRowDefError ,
1212 getTableMissingRowDefsError ,
1313 getTableMultipleDefaultRowDefsError ,
14- getTableUnknownColumnError
14+ getTableUnknownColumnError ,
15+ getTableUnknownDataSourceError
1516} from './table-errors' ;
1617import { CdkHeaderRowDef , CdkRowDef } from './row' ;
1718import { CdkColumnDef } from './cell' ;
@@ -45,6 +46,7 @@ describe('CdkTable', () => {
4546 BooleanRowCdkTableApp ,
4647 WrapperCdkTableApp ,
4748 OuterTableApp ,
49+ CdkTableWithDifferentDataInputsApp ,
4850 ] ,
4951 } ) . compileComponents ( ) ;
5052 } ) ) ;
@@ -113,6 +115,138 @@ describe('CdkTable', () => {
113115 } ) ;
114116 } ) ;
115117
118+ describe ( 'with different data inputs other than data source' , ( ) => {
119+ let dataInputFixture : ComponentFixture < CdkTableWithDifferentDataInputsApp > ;
120+ let dataInputComponent : CdkTableWithDifferentDataInputsApp ;
121+ let dataInputTableElement : HTMLElement ;
122+
123+ let baseData : TestData [ ] = [
124+ { a : 'a_1' , b : 'b_1' , c : 'c_1' } ,
125+ { a : 'a_2' , b : 'b_2' , c : 'c_2' } ,
126+ { a : 'a_3' , b : 'b_3' , c : 'c_3' } ,
127+ ] ;
128+
129+ beforeEach ( ( ) => {
130+ dataInputFixture = TestBed . createComponent ( CdkTableWithDifferentDataInputsApp ) ;
131+ dataInputComponent = dataInputFixture . componentInstance ;
132+ dataInputFixture . detectChanges ( ) ;
133+
134+ dataInputTableElement = dataInputFixture . nativeElement . querySelector ( 'cdk-table' ) ;
135+ } ) ;
136+
137+ it ( 'should render with data array input' , ( ) => {
138+ const data = baseData . slice ( ) ;
139+ dataInputComponent . dataSource = data ;
140+ dataInputFixture . detectChanges ( ) ;
141+
142+ const expectedRender = [
143+ [ 'Column A' , 'Column B' , 'Column C' ] ,
144+ [ 'a_1' , 'b_1' , 'c_1' ] ,
145+ [ 'a_2' , 'b_2' , 'c_2' ] ,
146+ [ 'a_3' , 'b_3' , 'c_3' ] ,
147+ ] ;
148+ expectTableToMatchContent ( dataInputTableElement , expectedRender ) ;
149+
150+ // Push data to the array but neglect to tell the table, should be no change
151+ data . push ( { a : 'a_4' , b : 'b_4' , c : 'c_4' } ) ;
152+
153+ expectTableToMatchContent ( dataInputTableElement , expectedRender ) ;
154+
155+ // Notify table of the change, expect another row
156+ dataInputComponent . table . renderRows ( ) ;
157+ dataInputFixture . detectChanges ( ) ;
158+
159+ expectedRender . push ( [ 'a_4' , 'b_4' , 'c_4' ] ) ;
160+ expectTableToMatchContent ( dataInputTableElement , expectedRender ) ;
161+
162+ // Remove a row and expect the change in rows
163+ data . pop ( ) ;
164+ dataInputComponent . table . renderRows ( ) ;
165+
166+ expectedRender . pop ( ) ;
167+ expectTableToMatchContent ( dataInputTableElement , expectedRender ) ;
168+
169+ // Remove the data input entirely and expect no rows - just header.
170+ dataInputComponent . dataSource = null ;
171+ dataInputFixture . detectChanges ( ) ;
172+
173+ expectTableToMatchContent ( dataInputTableElement , [ expectedRender [ 0 ] ] ) ;
174+
175+ // Add back the data to verify that it renders rows
176+ dataInputComponent . dataSource = data ;
177+ dataInputFixture . detectChanges ( ) ;
178+
179+ expectTableToMatchContent ( dataInputTableElement , expectedRender ) ;
180+ } ) ;
181+
182+ it ( 'should render with data stream input' , ( ) => {
183+ const data = baseData . slice ( ) ;
184+ const stream = new BehaviorSubject < TestData [ ] > ( data ) ;
185+ dataInputComponent . dataSource = stream ;
186+ dataInputFixture . detectChanges ( ) ;
187+
188+ const expectedRender = [
189+ [ 'Column A' , 'Column B' , 'Column C' ] ,
190+ [ 'a_1' , 'b_1' , 'c_1' ] ,
191+ [ 'a_2' , 'b_2' , 'c_2' ] ,
192+ [ 'a_3' , 'b_3' , 'c_3' ] ,
193+ ] ;
194+ expectTableToMatchContent ( dataInputTableElement , expectedRender ) ;
195+
196+ // Push data to the array and emit the data array on the stream
197+ data . push ( { a : 'a_4' , b : 'b_4' , c : 'c_4' } ) ;
198+ stream . next ( data ) ;
199+ dataInputFixture . detectChanges ( ) ;
200+
201+ expectedRender . push ( [ 'a_4' , 'b_4' , 'c_4' ] ) ;
202+ expectTableToMatchContent ( dataInputTableElement , expectedRender ) ;
203+
204+ // Push data to the array but rather than emitting, call renderRows.
205+ data . push ( { a : 'a_5' , b : 'b_5' , c : 'c_5' } ) ;
206+ dataInputComponent . table . renderRows ( ) ;
207+ dataInputFixture . detectChanges ( ) ;
208+
209+ expectedRender . push ( [ 'a_5' , 'b_5' , 'c_5' ] ) ;
210+ expectTableToMatchContent ( dataInputTableElement , expectedRender ) ;
211+
212+ // Remove a row and expect the change in rows
213+ data . pop ( ) ;
214+ expectedRender . pop ( ) ;
215+ stream . next ( data ) ;
216+
217+ expectTableToMatchContent ( dataInputTableElement , expectedRender ) ;
218+
219+ // Remove the data input entirely and expect no rows - just header.
220+ dataInputComponent . dataSource = null ;
221+ dataInputFixture . detectChanges ( ) ;
222+
223+ expectTableToMatchContent ( dataInputTableElement , [ expectedRender [ 0 ] ] ) ;
224+
225+ // Add back the data to verify that it renders rows
226+ dataInputComponent . dataSource = stream ;
227+ dataInputFixture . detectChanges ( ) ;
228+
229+ expectTableToMatchContent ( dataInputTableElement , expectedRender ) ;
230+ } ) ;
231+
232+ it ( 'should throw an error if the data source is not valid' , ( ) => {
233+ dataInputComponent . dataSource = { invalid : 'dataSource' } ;
234+
235+ expect ( ( ) => dataInputFixture . detectChanges ( ) )
236+ . toThrowError ( getTableUnknownDataSourceError ( ) . message ) ;
237+ } ) ;
238+
239+ it ( 'should throw an error if the data source is not valid' , ( ) => {
240+ dataInputComponent . dataSource = undefined ;
241+ dataInputFixture . detectChanges ( ) ;
242+
243+ // Expect the table to render just the header, no rows
244+ expectTableToMatchContent ( dataInputTableElement , [
245+ [ 'Column A' , 'Column B' , 'Column C' ]
246+ ] ) ;
247+ } ) ;
248+ } ) ;
249+
116250 it ( 'should render cells even if row data is falsy' , ( ) => {
117251 const booleanRowCdkTableAppFixture = TestBed . createComponent ( BooleanRowCdkTableApp ) ;
118252 const booleanRowCdkTableElement =
@@ -720,6 +854,36 @@ class SimpleCdkTableApp {
720854 @ViewChild ( CdkTable ) table : CdkTable < TestData > ;
721855}
722856
857+ @Component ( {
858+ template : `
859+ <cdk-table [dataSource]="dataSource">
860+ <ng-container cdkColumnDef="column_a">
861+ <cdk-header-cell *cdkHeaderCellDef> Column A</cdk-header-cell>
862+ <cdk-cell *cdkCellDef="let row"> {{row.a}}</cdk-cell>
863+ </ng-container>
864+
865+ <ng-container cdkColumnDef="column_b">
866+ <cdk-header-cell *cdkHeaderCellDef> Column B</cdk-header-cell>
867+ <cdk-cell *cdkCellDef="let row"> {{row.b}}</cdk-cell>
868+ </ng-container>
869+
870+ <ng-container cdkColumnDef="column_c">
871+ <cdk-header-cell *cdkHeaderCellDef> Column C</cdk-header-cell>
872+ <cdk-cell *cdkCellDef="let row"> {{row.c}}</cdk-cell>
873+ </ng-container>
874+
875+ <cdk-header-row *cdkHeaderRowDef="columnsToRender"></cdk-header-row>
876+ <cdk-row *cdkRowDef="let row; columns: columnsToRender"></cdk-row>
877+ </cdk-table>
878+ `
879+ } )
880+ class CdkTableWithDifferentDataInputsApp {
881+ dataSource : DataSource < TestData > | Observable < TestData [ ] > | TestData [ ] | any = null ;
882+ columnsToRender = [ 'column_a' , 'column_b' , 'column_c' ] ;
883+
884+ @ViewChild ( CdkTable ) table : CdkTable < TestData > ;
885+ }
886+
723887@Component ( {
724888 template : `
725889 <cdk-table [dataSource]="dataSource">
@@ -1186,6 +1350,9 @@ function expectTableToMatchContent(tableElement: Element, expectedTableContent:
11861350 }
11871351 }
11881352
1353+ // Copy the expected data array to avoid mutating the test's array
1354+ expectedTableContent = expectedTableContent . slice ( ) ;
1355+
11891356 // Check header cells
11901357 const expectedHeaderContent = expectedTableContent . shift ( ) ;
11911358 getHeaderCells ( tableElement ) . forEach ( ( cell , index ) => {
@@ -1196,7 +1363,13 @@ function expectTableToMatchContent(tableElement: Element, expectedTableContent:
11961363 } ) ;
11971364
11981365 // Check data row cells
1199- getRows ( tableElement ) . forEach ( ( row , rowIndex ) => {
1366+ const rows = getRows ( tableElement ) ;
1367+ if ( rows . length !== expectedTableContent . length ) {
1368+ missedExpectations . push (
1369+ `Expected ${ expectedTableContent . length } rows but found ${ rows . length } ` ) ;
1370+ fail ( missedExpectations . join ( '\n' ) ) ;
1371+ }
1372+ rows . forEach ( ( row , rowIndex ) => {
12001373 getCells ( row ) . forEach ( ( cell , cellIndex ) => {
12011374 const expected = expectedTableContent . length ?
12021375 expectedTableContent [ rowIndex ] [ cellIndex ] :
0 commit comments