Skip to content

Commit a2eefb4

Browse files
authored
Merge pull request #10022 from IgniteUI/ddincheva/rowStyling
Introduce conditional rowClasses and rowStyles
2 parents 19144c7 + d183919 commit a2eefb4

File tree

15 files changed

+280
-54
lines changed

15 files changed

+280
-54
lines changed

CHANGELOG.md

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,11 @@
22

33
All notable changes for each version of this project will be documented in this file.
44

5-
## 12.1.6
5+
## 12.2.0
6+
7+
### General
8+
- `igxGrid`, `igxHierarchicalGrid`, `igxTreeGrid`
9+
- 'oddRowCSS' and 'evenRowCSS' properties has been deprecated
610

711
### New Features
812
- `igxGrid`, `igxHierarchicalGrid`, `igxTreeGrid`
@@ -19,6 +23,7 @@ All notable changes for each version of this project will be documented in this
1923
this.treeGrid.beginAddRowByIndex(null); // spawns the add row UI as the first record
2024
```
2125
- Added capability to restore the state of multi column headers with `IgxGridStateDirective`.
26+
- Introduced new 'rowStyles' and 'rowClasses' grid properties which allows to define a custom styling on each grid row
2227

2328
## 12.1.3
2429

projects/igniteui-angular/src/lib/grids/common/grid-pipes.module.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,8 @@ import {
2020
IgxSummaryFormatterPipe,
2121
IgxGridAddRowPipe,
2222
IgxHeaderGroupWidthPipe,
23+
IgxGridRowClassesPipe,
24+
IgxGridRowStylesPipe,
2325
IgxHeaderGroupStylePipe
2426
} from './pipes';
2527

@@ -44,6 +46,8 @@ import {
4446
IgxColumnFormatterPipe,
4547
IgxSummaryFormatterPipe,
4648
IgxHeaderGroupWidthPipe,
49+
IgxGridRowClassesPipe,
50+
IgxGridRowStylesPipe,
4751
IgxHeaderGroupStylePipe
4852
],
4953
exports: [
@@ -66,6 +70,8 @@ import {
6670
IgxColumnFormatterPipe,
6771
IgxSummaryFormatterPipe,
6872
IgxHeaderGroupWidthPipe,
73+
IgxGridRowClassesPipe,
74+
IgxGridRowStylesPipe,
6975
IgxHeaderGroupStylePipe
7076
],
7177
imports: [

projects/igniteui-angular/src/lib/grids/common/pipes.ts

Lines changed: 93 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,16 @@ import { GridType } from './grid.interface';
77
import { IgxColumnComponent } from '../columns/column.component';
88
import { ColumnDisplayOrder } from './enums';
99
import { IgxColumnActionsComponent } from '../column-actions/column-actions.component';
10-
import { IgxSummaryOperand, IgxSummaryResult } from '../grid/public_api';
1110
import { IgxAddRow } from './crud.service';
11+
import { IgxGridRow } from '../grid-public-row';
12+
import { IgxTreeGridRowComponent } from '../tree-grid/tree-grid-row.component';
13+
import { IgxGridRowComponent } from '../grid/grid-row.component';
14+
import { IgxHierarchicalRowComponent } from '../hierarchical-grid/hierarchical-row.component';
15+
import { IgxSummaryOperand, IgxSummaryResult } from '../summaries/grid-summary';
16+
17+
interface CSSProp {
18+
[prop: string]: any;
19+
}
1220

1321
/**
1422
* @hidden
@@ -19,7 +27,7 @@ import { IgxAddRow } from './crud.service';
1927
})
2028
export class IgxGridCellStyleClassesPipe implements PipeTransform {
2129

22-
public transform(cssClasses: { [prop: string]: any }, _: any, data: any, field: string, index: number, __: number): string {
30+
public transform(cssClasses: CSSProp, _: any, data: any, field: string, index: number, __: number): string {
2331
if (!cssClasses) {
2432
return '';
2533
}
@@ -48,8 +56,7 @@ export class IgxGridCellStyleClassesPipe implements PipeTransform {
4856
})
4957
export class IgxGridCellStylesPipe implements PipeTransform {
5058

51-
public transform(styles: { [prop: string]: any }, _: any, data: any, field: string, index: number, __: number):
52-
{ [prop: string]: any } {
59+
public transform(styles: CSSProp, _: any, data: any, field: string, index: number, __: number): CSSProp {
5360
const css = {};
5461
if (!styles) {
5562
return css;
@@ -64,6 +71,87 @@ export class IgxGridCellStylesPipe implements PipeTransform {
6471
}
6572
}
6673

74+
type _RowType = IgxGridRowComponent | IgxTreeGridRowComponent | IgxHierarchicalRowComponent;
75+
76+
/**
77+
* @hidden
78+
* @internal
79+
*/
80+
@Pipe({ name: 'igxGridRowClasses' })
81+
export class IgxGridRowClassesPipe implements PipeTransform {
82+
public row: IgxGridRow;
83+
84+
constructor(private gridAPI: GridBaseAPIService<IgxGridBaseDirective & GridType>) {
85+
this.row = new IgxGridRow(this.gridAPI.grid as any, -1, {});
86+
}
87+
88+
public transform(
89+
cssClasses: CSSProp,
90+
row: _RowType,
91+
editMode: boolean,
92+
selected: boolean,
93+
dirty: boolean,
94+
deleted: boolean,
95+
dragging: boolean,
96+
index: number,
97+
mrl: boolean,
98+
filteredOut: boolean,
99+
_: number
100+
) {
101+
const result = new Set(['igx-grid__tr', index % 2 ? row.grid.evenRowCSS : row.grid.oddRowCSS]);
102+
const mapping = [
103+
[selected, 'igx-grid__tr--selected'],
104+
[editMode, 'igx-grid__tr--edit'],
105+
[dirty, 'igx-grid__tr--edited'],
106+
[deleted, 'igx-grid__tr--deleted'],
107+
[dragging, 'igx-grid__tr--drag'],
108+
[mrl, 'igx-grid__tr--mrl'],
109+
// Tree grid only
110+
[filteredOut, 'igx-grid__tr--filtered']
111+
];
112+
113+
for (const [state, _class] of mapping) {
114+
if (state) {
115+
result.add(_class as string);
116+
}
117+
}
118+
119+
for (const cssClass of Object.keys(cssClasses ?? {})) {
120+
const callbackOrValue = cssClasses[cssClass];
121+
this.row.index = index;
122+
(this.row as any)._data = row.rowData;
123+
const apply = typeof callbackOrValue === 'function' ? callbackOrValue(this.row) : callbackOrValue;
124+
if (apply) {
125+
result.add(cssClass);
126+
}
127+
}
128+
return result;
129+
}
130+
}
131+
132+
/**
133+
* @hidden
134+
* @internal
135+
*/
136+
@Pipe({ name: 'igxGridRowStyles' })
137+
export class IgxGridRowStylesPipe implements PipeTransform {
138+
139+
constructor(private gridAPI: GridBaseAPIService<IgxGridBaseDirective & GridType>) { }
140+
141+
public transform(styles: CSSProp, rowData: any, index: number, __: number): CSSProp {
142+
const css = {};
143+
if (!styles) {
144+
return css;
145+
}
146+
for (const prop of Object.keys(styles)) {
147+
const cb = styles[prop];
148+
const row = new IgxGridRow((this.gridAPI.grid as any), index, rowData);
149+
css[prop] = typeof cb === 'function' ? cb(row) : cb;
150+
}
151+
return css;
152+
}
153+
}
154+
67155
/**
68156
* @hidden
69157
* @internal
@@ -341,7 +429,7 @@ export class IgxGridAddRowPipe implements PipeTransform {
341429
@Pipe({ name: 'igxHeaderGroupWidth' })
342430
export class IgxHeaderGroupWidthPipe implements PipeTransform {
343431

344-
public transform(width: any, minWidth: any, hasLayout: boolean ) {
432+
public transform(width: any, minWidth: any, hasLayout: boolean) {
345433
return hasLayout ? '' : `${Math.max(parseFloat(width), minWidth)}px`;
346434
}
347435
}

projects/igniteui-angular/src/lib/grids/grid-base.directive.ts

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -343,6 +343,7 @@ export abstract class IgxGridBaseDirective extends DisplayDensityBase implements
343343
* <igx-grid #grid [data]="Data" [evenRowCSS]="'igx-grid--my-even-class'" [autoGenerate]="true"></igx-grid>
344344
* ```
345345
*/
346+
@DeprecateProperty('`evenRowCSS` is deprecated. We suggest using `rowClasses` property instead.')
346347
@Input()
347348
public evenRowCSS = 'igx-grid__tr--even';
348349

@@ -354,9 +355,47 @@ export abstract class IgxGridBaseDirective extends DisplayDensityBase implements
354355
* <igx-grid #grid [data]="Data" [evenRowCSS]="'igx-grid--my-odd-class'" [autoGenerate]="true"></igx-grid>
355356
* ```
356357
*/
358+
@DeprecateProperty('`oddRowCSS` is deprecated. We suggest using `rowClasses` property instead.')
357359
@Input()
358360
public oddRowCSS = 'igx-grid__tr--odd';
359361

362+
/**
363+
* Sets a conditional class selector to the grid's row element.
364+
* Accepts an object literal, containing key-value pairs,
365+
* where the key is the name of the CSS class and the value is
366+
* either a callback function that returns a boolean, or boolean, like so:
367+
* ```typescript
368+
* callback = (row: RowType) => { return row.selected > 6; }
369+
* rowClasses = { 'className' : this.callback };
370+
* ```
371+
* ```html
372+
* <igx-grid #grid [data]="Data" [rowClasses] = "rowClasses" [autoGenerate]="true"></igx-grid>
373+
* ```
374+
*
375+
* @memberof IgxColumnComponent
376+
*/
377+
@Input()
378+
public rowClasses: any;
379+
380+
/**
381+
* Sets conditional style properties on the grid row element.
382+
* It accepts an object literal where the keys are
383+
* the style properties and the value is an expression to be evaluated.
384+
* ```typescript
385+
* styles = {
386+
* background: 'yellow',
387+
* color: (row: RowType) => row.selected : 'red': 'white'
388+
* }
389+
* ```
390+
* ```html
391+
* <igx-grid #grid [data]="Data" [rowStyles]="styles" [autoGenerate]="true"></igx-grid>
392+
* ```
393+
*
394+
* @memberof IgxColumnComponent
395+
*/
396+
@Input()
397+
public rowStyles = null;
398+
360399
/**
361400
* Gets/Sets the primary key.
362401
*

projects/igniteui-angular/src/lib/grids/grid/grid-row-pinning.spec.ts

Lines changed: 47 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,13 +10,12 @@ import { IPinningConfig } from '../grid.common';
1010
import { SampleTestData } from '../../test-utils/sample-test-data.spec';
1111
import { GridFunctions } from '../../test-utils/grid-functions.spec';
1212
import { SortingDirection } from '../../data-operations/sorting-expression.interface';
13-
import { IgxGridTransaction } from '../tree-grid/public_api';
14-
import { IgxTransactionService } from '../../services/public_api';
1513
import { GridSummaryFunctions } from '../../test-utils/grid-functions.spec';
1614
import { IgxStringFilteringOperand } from '../../data-operations/filtering-condition';
1715
import { IgxPaginatorComponent } from '../../paginator/paginator.component';
1816
import { wait, UIInteractions } from '../../test-utils/ui-interactions.spec';
1917
import { setupGridScrollDetection } from '../../test-utils/helper-utils.spec';
18+
import { GridRowConditionalStylingComponent } from '../../test-utils/grid-base-components.spec';
2019

2120
describe('Row Pinning #grid', () => {
2221
const FIXED_ROW_CONTAINER = '.igx-grid__tr--pinned ';
@@ -33,7 +32,8 @@ describe('Row Pinning #grid', () => {
3332
GridRowPinningWithMRLComponent,
3433
GridRowPinningWithMDVComponent,
3534
GridRowPinningWithTransactionsComponent,
36-
GridRowPinningWithInitialPinningComponent
35+
GridRowPinningWithInitialPinningComponent,
36+
GridRowConditionalStylingComponent
3737
],
3838
imports: [
3939
NoopAnimationsModule,
@@ -1265,6 +1265,50 @@ describe('Row Pinning #grid', () => {
12651265
expect(grid.hasPinnedRecords).toBeTrue();
12661266
});
12671267
});
1268+
1269+
describe('Conditional row styling', () => {
1270+
1271+
beforeEach(fakeAsync(() => {
1272+
fix = TestBed.createComponent(GridRowConditionalStylingComponent);
1273+
fix.detectChanges();
1274+
grid = fix.componentInstance.grid;
1275+
tick();
1276+
fix.detectChanges();
1277+
}));
1278+
1279+
it('Shoud be able to conditionally style rows. Check is the class present in the row native element class list', () => {
1280+
fix.detectChanges();
1281+
const firstRow = grid.gridAPI.get_row_by_index(0);
1282+
const fourthRow = grid.gridAPI.get_row_by_index(3);
1283+
1284+
expect(firstRow).toBeDefined();
1285+
expect(firstRow.element.nativeElement.classList.contains('eventRow')).toBeTrue();
1286+
expect(firstRow.element.nativeElement.classList.contains('oddRow')).toBeFalse();
1287+
expect(fourthRow.element.nativeElement.classList.contains('eventRow')).toBeFalse();
1288+
expect(fourthRow.element.nativeElement.classList.contains('oddRow')).toBeTrue();
1289+
});
1290+
1291+
it('Should apply custom CSS bindings to the grid cells/rows. Check the style attribute to match each binding', () => {
1292+
const evenColStyles = {
1293+
background: (row) => row.index % 2 === 0 ? 'gray' : 'white',
1294+
animation: '0.75s popin'
1295+
};
1296+
1297+
fix.detectChanges();
1298+
grid.rowStyles = evenColStyles;
1299+
grid.notifyChanges(true);
1300+
fix.detectChanges();
1301+
const firstRow = grid.gridAPI.get_row_by_index(0);
1302+
const fourthRow = grid.gridAPI.get_row_by_index(3);
1303+
1304+
const expectedEvenStyles = 'background: gray; animation: 0.75s ease 0s 1 normal none running popin;';
1305+
const expectedOddStyles = 'background: white; animation: 0.75s ease 0s 1 normal none running popin;';
1306+
1307+
expect(firstRow.element.nativeElement.style.cssText).toEqual(expectedEvenStyles);
1308+
expect(fourthRow.element.nativeElement.style.cssText).toEqual(expectedOddStyles);
1309+
});
1310+
1311+
});
12681312
});
12691313

12701314
@Component({

projects/igniteui-angular/src/lib/grids/grid/grid-row.component.ts

Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { Component, forwardRef, ChangeDetectionStrategy, HostBinding } from '@angular/core';
1+
import { Component, forwardRef, ChangeDetectionStrategy } from '@angular/core';
22
import { IgxGridComponent } from './grid.component';
33
import { IgxRowDirective } from '../row.directive';
44

@@ -10,11 +10,6 @@ import { IgxRowDirective } from '../row.directive';
1010
})
1111
export class IgxGridRowComponent extends IgxRowDirective<IgxGridComponent> {
1212

13-
@HostBinding('class.igx-grid__tr--mrl')
14-
public get hasColumnLayouts(): boolean {
15-
return this.grid.hasColumnLayouts;
16-
}
17-
1813
public getContext(col, row) {
1914
return {
2015
$implicit: col,

projects/igniteui-angular/src/lib/grids/grid/grid.component.html

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -93,11 +93,15 @@
9393
<ng-container *ngTemplateOutlet="hasPinnedRecords && !isRowPinningToTop ? pinnedRecordsTemplate : null">
9494
</ng-container>
9595
<ng-template #record_template let-rowIndex="index" let-rowData let-disabledRow="disabled">
96-
<igx-grid-row [gridID]="id" [index]="rowIndex" [rowData]="rowData" [disabled]="disabledRow" #row>
96+
<igx-grid-row [gridID]="id" [index]="rowIndex" [rowData]="rowData" [disabled]="disabledRow"
97+
[ngClass]="rowClasses | igxGridRowClasses:row:row.inEditMode:row.selected:row.dirty:row.deleted:row.dragging:rowIndex:hasColumnLayouts:false:pipeTrigger"
98+
[ngStyle]="rowStyles | igxGridRowStyles:rowData:rowIndex:pipeTrigger" #row>
9799
</igx-grid-row>
98100
</ng-template>
99101
<ng-template #pinned_record_template let-rowIndex="index" let-rowData>
100-
<igx-grid-row [gridID]="id" [index]="rowIndex" [rowData]="rowData" #row #pinnedRow>
102+
<igx-grid-row [gridID]="id" [index]="rowIndex" [rowData]="rowData"
103+
[ngClass]="rowClasses | igxGridRowClasses:row:row.inEditMode:row.selected:row.dirty:row.deleted:row.dragging:rowIndex:hasColumnLayouts:false:pipeTrigger"
104+
[ngStyle]="rowStyles | igxGridRowStyles:rowData:rowIndex:pipeTrigger"#row #pinnedRow>
101105
</igx-grid-row>
102106
</ng-template>
103107
<ng-template #group_template let-rowIndex="index" let-rowData>

projects/igniteui-angular/src/lib/grids/hierarchical-grid/hierarchical-grid.component.html

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -67,12 +67,16 @@
6767
<!-- <ng-container *igxTemplateOutlet="(isHierarchicalRecord(rowData) ? hierarchical_record_template : (isChildGridRecord(rowData) && isExpanded(rowData) ? child_record_template : hierarchical_record_template)); context: getContext(rowData)"></ng-container> -->
6868
</ng-template>
6969
<ng-template #hierarchical_record_template let-rowIndex="index" let-disabledRow="disabled" let-rowData>
70-
<igx-hierarchical-grid-row [gridID]="id" [index]="rowIndex" [disabled]="disabledRow" [rowData]="rowData" #row>
70+
<igx-hierarchical-grid-row [gridID]="id" [index]="rowIndex" [disabled]="disabledRow" [rowData]="rowData"
71+
[ngClass]="rowClasses | igxGridRowClasses:row:row.inEditMode:row.selected:row.dirty:row.deleted:row.dragging:rowIndex:hasColumnLayouts:false:pipeTrigger"
72+
[ngStyle]="rowStyles | igxGridRowStyles:rowData:rowIndex:pipeTrigger" #row>
7173
</igx-hierarchical-grid-row>
7274
</ng-template>
7375

7476
<ng-template #pinned_hierarchical_record_template let-rowIndex="index" let-rowData>
75-
<igx-hierarchical-grid-row [gridID]="id" [index]="rowIndex" [rowData]="rowData" #row #pinnedRow>
77+
<igx-hierarchical-grid-row [gridID]="id" [index]="rowIndex" [rowData]="rowData"
78+
[ngClass]="rowClasses | igxGridRowClasses:row:row.inEditMode:row.selected:row.dirty:row.deleted:row.dragging:rowIndex:hasColumnLayouts:false:pipeTrigger"
79+
[ngStyle]="rowStyles | igxGridRowStyles:rowData:rowIndex:pipeTrigger" #row #pinnedRow>
7680
</igx-hierarchical-grid-row>
7781
</ng-template>
7882
<ng-template #child_record_template let-rowIndex="index" let-rowData>

0 commit comments

Comments
 (0)