Skip to content

Commit 6b2d47e

Browse files
committed
Merge branch 'pivot-grid-master' of https://github.com/IgniteUI/igniteui-angular into mdragnev/pivot-sample
2 parents cde7a2d + 72bae81 commit 6b2d47e

30 files changed

+1005
-1164
lines changed

.github/workflows/nodejs.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@ jobs:
4141
npm run test:lib
4242
npm run test:styles
4343
npm run test:schematics
44+
npm run test:i18n
4445
env:
4546
NODE_OPTIONS: --max_old_space_size=4096
4647
- name: Build i18n & validate output

CHANGELOG.md

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,12 @@ All notable changes for each version of this project will be documented in this
3535
- `IgxColumnActionsComponent`
3636
- **Breaking Change** - The following input has been removed
3737
- Input `columns`. Use `igxGrid` `columns` input instead.
38+
39+
## 12.2.3
40+
41+
### General
42+
- **Breaking Change** - `IgxPercentSummaryOperand` and `IgxCurrencySummaryOperand` have been removed and `IgxNumberSummaryOperand` should be used instead. If you have used the percent or currency summary operands to extend a custom summary operand from them, then change the custom operand to extend from the number summary operand.
43+
3844
## 12.2.1
3945

4046
### New Features
@@ -3659,4 +3665,4 @@ export class IgxCustomFilteringOperand extends IgxFilteringOperand {
36593665
- `IgxDraggableDirective` moved inside `../directives/dragdrop/` folder
36603666
- `IgxRippleDirective` moved inside `../directives/ripple/` folder
36613667
- Folder `"./navigation/nav-service"` renamed to `"./navigation/nav.service"`
3662-
3668+

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
"test:lib:watch": "ng test igniteui-angular --karma-config=./projects/igniteui-angular/karma.watch.conf.js",
2424
"test:schematics": "ts-node --project projects/igniteui-angular/migrations/tsconfig.json ./node_modules/jasmine/bin/jasmine.js ./projects/igniteui-angular/migrations/**/*.spec.ts ./projects/igniteui-angular/schematics/**/*.spec.ts",
2525
"test:styles": "ts-node --skip-project ./node_modules/jasmine/bin/jasmine.js ./projects/igniteui-angular/src/lib/core/styles/spec/tests.ts",
26+
"test:i18n": "ts-node --skip-project ./projects/igniteui-angular/src/lib/core/i18n/tests/tests.ts",
2627
"build:lib": "ng build igniteui-angular --configuration production && gulp buildStyle",
2728
"build:style": "gulp buildStyle",
2829
"build:migration": "gulp copyMigrations && tsc --listEmittedFiles --project ./projects/igniteui-angular/migrations/tsconfig.json",
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
import * as fs from 'fs';
2+
import * as path from 'path';
3+
4+
const i18nProductPath = path.join(__dirname, '../');
5+
const i18nLanguagesPath = path.join(__dirname, '../../../../../../igniteui-angular-i18n/src/i18n');
6+
const errors = [];
7+
8+
class i18nTests {
9+
public runTests(): void {
10+
this.i18nFilesMatchForAllLanguages();
11+
}
12+
13+
public getDirectories = srcPath => fs.readdirSync(srcPath).filter(file => fs.statSync(path.join(srcPath, file)).isDirectory());
14+
public getFiles = srcPath => fs.readdirSync(srcPath).filter(file => fs.statSync(path.join(srcPath, file)).isFile());
15+
16+
public i18nFilesMatchForAllLanguages(): void {
17+
this.getDirectories(i18nLanguagesPath).forEach(dir => {
18+
const curDirPath = path.join(i18nLanguagesPath, dir);
19+
if (this.getFiles(curDirPath).length !== this.getFiles(i18nProductPath).length) {
20+
errors.push(`Not all i18n component files that are available for localization have matching files for ${dir} language.
21+
Check and add the appropriate resource strings with EN translation and mark the PR as 'pending localization'`
22+
);
23+
}
24+
});
25+
if (errors.length > 0) {
26+
throw errors;
27+
}
28+
}
29+
}
30+
31+
new i18nTests().runTests();

projects/igniteui-angular/src/lib/core/styles/components/slider/_slider-theme.scss

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -340,7 +340,6 @@
340340
width: 100%;
341341
height: rem($slider-track-height);
342342
background-size: 100% em($slider-track-height);
343-
background-color: --var($theme, 'track-step-color');
344343
opacity: .85;
345344
transition: opacity .2s $ease-out-quad;
346345
z-index: 1;
Lines changed: 153 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,153 @@
1+
2+
import { IPivotDimension, IPivotKeys, IPivotValue, PivotDimensionType } from '../grids/pivot-grid/pivot-grid.interface';
3+
import { PivotUtil } from '../grids/pivot-grid/pivot-util';
4+
5+
export interface IPivotDimensionStrategy {
6+
process(collection: any,
7+
dimensions: IPivotDimension[],
8+
values: IPivotValue[],
9+
pivotKeys?: IPivotKeys): any[];
10+
}
11+
12+
export class NoopPivotDimensionsStrategy implements IPivotDimensionStrategy {
13+
private static _instance: NoopPivotDimensionsStrategy = null;
14+
15+
public static instance() {
16+
return this._instance || (this._instance = new NoopPivotDimensionsStrategy());
17+
}
18+
19+
public process(collection: any[], _: IPivotDimension[], __: IPivotValue[]): any[] {
20+
return collection;
21+
}
22+
}
23+
24+
25+
export class PivotRowDimensionsStrategy implements IPivotDimensionStrategy {
26+
private static _instance: PivotRowDimensionsStrategy = null;
27+
28+
public static instance() {
29+
return this._instance || (this._instance = new PivotRowDimensionsStrategy());
30+
}
31+
32+
public process(
33+
collection: any,
34+
rows: IPivotDimension[],
35+
values?: IPivotValue[],
36+
pivotKeys: IPivotKeys =
37+
{ aggregations: 'aggregations', records: 'records', children: 'children', level: 'level'}
38+
): any[] {
39+
let hierarchies;
40+
let data;
41+
const prevRowDims = [];
42+
let prevDim;
43+
for (const row of rows) {
44+
if (!data) {
45+
// build hierarchies - groups and subgroups
46+
hierarchies = PivotUtil.getFieldsHierarchy(collection, [row], PivotDimensionType.Row, pivotKeys);
47+
// generate flat data from the hierarchies
48+
data = PivotUtil.processHierarchy(hierarchies, collection[0] ?? [], pivotKeys, 0, true);
49+
row.fieldName = hierarchies.get(hierarchies.keys().next().value).dimension.fieldName;
50+
prevRowDims.push(row);
51+
prevDim = row;
52+
} else {
53+
const newData = [...data];
54+
for (let i = 0; i < newData.length; i++) {
55+
const currData = newData[i][prevDim.fieldName + '_' + pivotKeys.records];
56+
const hierarchyFields = PivotUtil
57+
.getFieldsHierarchy(currData, [row], PivotDimensionType.Row, pivotKeys);
58+
const siblingData = PivotUtil
59+
.processHierarchy(hierarchyFields, newData[i] ?? [], pivotKeys, 0);
60+
row.fieldName = hierarchyFields.get(hierarchyFields.keys().next().value).dimension.fieldName;
61+
PivotUtil.processSiblingProperties(newData[i], siblingData, pivotKeys);
62+
63+
PivotUtil.processSubGroups(row, prevRowDims.slice(0), siblingData, pivotKeys);
64+
if (PivotUtil.getDimensionDepth(prevDim) > PivotUtil.getDimensionDepth(row)) {
65+
newData[i][row.fieldName + '_' + pivotKeys.records] = siblingData;
66+
} else {
67+
newData.splice(i , 1, ...siblingData);
68+
}
69+
i += siblingData.length - 1;
70+
}
71+
data = newData;
72+
prevDim = row;
73+
prevRowDims.push(row);
74+
}
75+
}
76+
return data;
77+
}
78+
}
79+
80+
export class PivotColumnDimensionsStrategy implements IPivotDimensionStrategy {
81+
private static _instance: PivotRowDimensionsStrategy = null;
82+
83+
public static instance() {
84+
return this._instance || (this._instance = new PivotColumnDimensionsStrategy());
85+
}
86+
87+
public process(
88+
collection: any[],
89+
columns: IPivotDimension[],
90+
values: IPivotValue[],
91+
pivotKeys: IPivotKeys = {aggregations: 'aggregations', records: 'records', children: 'children', level: 'level'}
92+
): any[] {
93+
const result = [];
94+
collection.forEach(hierarchy => {
95+
// apply aggregations based on the created groups and generate column fields based on the hierarchies
96+
this.groupColumns(hierarchy, columns, values, pivotKeys);
97+
if (hierarchy[pivotKeys.children]) {
98+
let flatCols = {};
99+
PivotUtil.flattenColumnHierarchy(hierarchy[pivotKeys.children], values, pivotKeys).forEach(o => {
100+
delete o[pivotKeys.records];
101+
flatCols = {...flatCols, ...o};
102+
});
103+
delete hierarchy[pivotKeys.children]; /* or we can keep it
104+
and use when creating the columns in pivot grid instead of recreating it */
105+
const keys = Object.keys(hierarchy);
106+
//remove all record keys from final data since we don't need them anymore.
107+
keys.forEach(k => {
108+
if (k.indexOf(pivotKeys.records) !== -1 || k === pivotKeys.level) {
109+
delete hierarchy[k];
110+
}
111+
});
112+
if (this.isLeaf(hierarchy, pivotKeys)) {
113+
delete hierarchy[pivotKeys.records]; /* remove the helper records of the actual records so that
114+
expand indicators can be rendered properly */
115+
}
116+
for (const property in flatCols) {
117+
if (flatCols.hasOwnProperty(property)) {
118+
hierarchy[property] = flatCols[property];
119+
}
120+
}
121+
result.push(hierarchy);
122+
}
123+
});
124+
return result;
125+
}
126+
127+
private groupColumns(hierarchy, columns, values, pivotKeys) {
128+
const children = hierarchy[pivotKeys.children];
129+
if (children) {
130+
this.groupColumns(children, columns, values, pivotKeys);
131+
} else if (hierarchy[pivotKeys.records]) {
132+
const leafRecords = this.getLeafs(hierarchy[pivotKeys.records], pivotKeys);
133+
hierarchy[pivotKeys.children] = PivotUtil.getFieldsHierarchy(leafRecords, columns, PivotDimensionType.Column, pivotKeys);
134+
PivotUtil.applyAggregations(hierarchy[pivotKeys.children], values, pivotKeys);
135+
}
136+
}
137+
138+
private getLeafs(records, pivotKeys) {
139+
let leafs = [];
140+
for (const rec of records) {
141+
if (rec[pivotKeys.records]) {
142+
leafs = leafs.concat(this.getLeafs(rec[pivotKeys.records], pivotKeys));
143+
} else {
144+
leafs.push(rec);
145+
}
146+
}
147+
return leafs;
148+
}
149+
150+
private isLeaf(record, pivotKeys) {
151+
return !(record[pivotKeys.records] && record[pivotKeys.records].some(r => r[pivotKeys.records]));
152+
}
153+
}

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

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -163,7 +163,7 @@ export interface IRowToggleEventArgs extends IBaseEventArgs {
163163
/**
164164
* Event emitted when a row's pin state changes.
165165
*/
166-
export interface IPinRowEventArgs extends IBaseEventArgs {
166+
export interface IPinRowEventArgs extends IBaseEventArgs, CancelableEventArgs {
167167
/**
168168
* The ID of the row, that was pinned/unpinned.
169169
* ID is either the primaryKey value or the data record instance.
@@ -172,7 +172,7 @@ export interface IPinRowEventArgs extends IBaseEventArgs {
172172
row?: RowType;
173173
/** The index at which to pin the row in the pinned rows collection. */
174174
insertAtIndex?: number;
175-
/** Whether or noy the row is pinned or unpinned. */
175+
/** Whether or not the row is pinned or unpinned. */
176176
readonly isPinned: boolean;
177177
}
178178

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

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4655,10 +4655,15 @@ export abstract class IgxGridBaseDirective extends DisplayDensityBase implements
46554655
insertAtIndex: index,
46564656
isPinned: true,
46574657
rowID,
4658-
row
4658+
row,
4659+
cancel: false
46594660
};
46604661
this.rowPinning.emit(eventArgs);
46614662

4663+
if (eventArgs.cancel) {
4664+
return;
4665+
}
4666+
46624667
this.crudService.endEdit(false);
46634668

46644669
const insertIndex = typeof eventArgs.insertAtIndex === 'number' ? eventArgs.insertAtIndex : this._pinnedRecordIDs.length;
@@ -4691,10 +4696,15 @@ export abstract class IgxGridBaseDirective extends DisplayDensityBase implements
46914696
const eventArgs: IPinRowEventArgs = {
46924697
isPinned: false,
46934698
rowID,
4694-
row
4699+
row,
4700+
cancel: false
46954701
};
46964702
this.rowPinning.emit(eventArgs);
46974703

4704+
if (eventArgs.cancel) {
4705+
return;
4706+
}
4707+
46984708
this.crudService.endEdit(false);
46994709
this._pinnedRecordIDs.splice(index, 1);
47004710
this.pipeTrigger++;

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

Lines changed: 61 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import { TestBed, fakeAsync, tick } from '@angular/core/testing';
33
import { By } from '@angular/platform-browser';
44
import { NoopAnimationsModule } from '@angular/platform-browser/animations';
55
import { IgxGridComponent } from './grid.component';
6-
import { IgxGridModule } from './public_api';
6+
import { IgxGridModule, IPinRowEventArgs } from './public_api';
77
import { configureTestSuite } from '../../test-utils/configure-suite';
88
import { ColumnPinningPosition, RowPinningPosition } from '../common/enums';
99
import { IPinningConfig } from '../grid.common';
@@ -164,7 +164,8 @@ describe('Row Pinning #grid', () => {
164164
rowID,
165165
insertAtIndex: 0,
166166
isPinned: true,
167-
row
167+
row,
168+
cancel: false
168169
});
169170

170171
row = grid.getRowByIndex(0);
@@ -190,7 +191,8 @@ describe('Row Pinning #grid', () => {
190191
rowID,
191192
insertAtIndex: 0,
192193
isPinned: true,
193-
row
194+
row,
195+
cancel: false
194196
});
195197

196198
row.unpin();
@@ -200,6 +202,62 @@ describe('Row Pinning #grid', () => {
200202
expect(grid.rowPinned.emit).toHaveBeenCalledTimes(2);
201203
});
202204

205+
it(`Should be able to cancel rowPinning on pin/unpin event.`, () => {
206+
spyOn(grid.rowPinning, 'emit').and.callThrough();
207+
let sub = grid.rowPinning.subscribe((e: IPinRowEventArgs) => {
208+
e.cancel = true;
209+
});
210+
211+
const row = grid.getRowByIndex(0);
212+
const rowID = row.key;
213+
expect(row.pinned).toBeFalsy();
214+
215+
row.pin();
216+
fix.detectChanges();
217+
218+
expect(grid.rowPinning.emit).toHaveBeenCalledTimes(1);
219+
expect(grid.rowPinning.emit).toHaveBeenCalledWith({
220+
insertAtIndex: 0,
221+
isPinned: true,
222+
rowID,
223+
row,
224+
cancel: true
225+
});
226+
expect(row.pinned).toBeFalsy();
227+
228+
sub.unsubscribe();
229+
230+
row.pin();
231+
fix.detectChanges();
232+
233+
expect(grid.rowPinning.emit).toHaveBeenCalledTimes(2);
234+
expect(grid.rowPinning.emit).toHaveBeenCalledWith({
235+
insertAtIndex: 0,
236+
isPinned: true,
237+
rowID,
238+
row,
239+
cancel: false
240+
});
241+
expect(row.pinned).toBe(true);
242+
243+
sub = grid.rowPinning.subscribe((e: IPinRowEventArgs) => {
244+
e.cancel = true;
245+
});
246+
247+
row.unpin();
248+
fix.detectChanges();
249+
250+
expect(grid.rowPinning.emit).toHaveBeenCalledTimes(3);
251+
expect(grid.rowPinning.emit).toHaveBeenCalledWith({
252+
isPinned: false,
253+
rowID,
254+
row,
255+
cancel: true
256+
});
257+
expect(row.pinned).toBe(true);
258+
sub.unsubscribe();
259+
});
260+
203261
it('should pin/unpin via grid API methods.', () => {
204262
// pin 2nd row
205263
grid.pinRow(fix.componentInstance.data[1]);

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

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -32,8 +32,9 @@
3232
class="igx-grid__scroll-on-drag-pinned" [style.left.px]="pinnedWidth"></span>
3333
<ng-template igxGridFor let-rowData [igxGridForOf]="data
3434
| pivotGridFilter:pivotConfiguration.filters:filterStrategy:advancedFilteringExpressionsTree
35-
| pivotGridRow:pivotConfiguration.rows:expansionStates:pivotConfiguration.values
36-
| pivotGridColumn:pivotConfiguration.columns:pivotConfiguration.values"
35+
| pivotGridRow:pivotConfiguration:expansionStates
36+
| pivotGridRowExpansion:pivotConfiguration:expansionStates
37+
| pivotGridColumn:pivotConfiguration:expansionStates"
3738
let-rowIndex="index" [igxForScrollOrientation]="'vertical'" [igxForScrollContainer]='verticalScroll'
3839
[igxForContainerSize]='calcHeight'
3940
[igxForItemSize]="hasColumnLayouts ? rowHeight * multiRowLayoutRowSize + 1 : renderedRowHeight"

0 commit comments

Comments
 (0)