Skip to content

Commit 8ce70ca

Browse files
authored
Merge branch '19.1.x' into mpopov/calendar/bug/19-1-0/fix-for-15409
2 parents 974bd90 + 12162d7 commit 8ce70ca

File tree

8 files changed

+71
-5
lines changed

8 files changed

+71
-5
lines changed
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
import { getUUID } from './random';
2+
3+
describe('Random (crypto.randomUuid()) fallback unit tests', () => {
4+
let originalRandomUuid = crypto.randomUUID;
5+
6+
beforeAll(() => {
7+
crypto.randomUUID = null; // Mock crypto.randomUUID to simulate a non-secure context
8+
});
9+
10+
it('should generate a valid UUID', () => {
11+
const uuid = getUUID();
12+
expect(uuid).toMatch(/^[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/);
13+
});
14+
15+
it('should generate unique UUIDs', () => {
16+
const uuids = new Set();
17+
for (let i = 0; i < 100; i++) {
18+
uuids.add(getUUID());
19+
}
20+
expect(uuids.size).toBe(100); // All UUIDs should be unique
21+
});
22+
23+
afterAll(() => {
24+
crypto.randomUUID = originalRandomUuid; // Restore the original function
25+
});
26+
});
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
/**
2+
* Use the function to get a random UUID string when secure context is not guaranteed making crypto.randomUUID unavailable.
3+
* @returns A random UUID string.
4+
*/
5+
export function getUUID(): `${string}-${string}-${string}-${string}-${string}` {
6+
if (typeof crypto.randomUUID === 'function') {
7+
return crypto.randomUUID();
8+
}
9+
// Secure fallback using crypto.getRandomValues()
10+
const bytes = new Uint8Array(16);
11+
crypto.getRandomValues(bytes);
12+
13+
// Set version (4) and variant (RFC 4122)
14+
bytes[6] = (bytes[6] & 0x0f) | 0x40; // Version 4
15+
bytes[8] = (bytes[8] & 0x3f) | 0x80; // Variant 1
16+
17+
const a = [...bytes].map((b) => b.toString(16).padStart(2, '0')).join('');
18+
return `${a.slice(0, 8)}-${a.slice(8, 12)}-${a.slice(12, 16)}-${a.slice(16, 20)}-${a.slice(20)}`;
19+
}

projects/igniteui-angular/src/lib/grids/filtering/excel-style/common.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import { isTree } from '../../../data-operations/expressions-tree-util';
22
import { FilteringLogic, IFilteringExpression } from '../../../data-operations/filtering-expression.interface';
33
import { IFilteringExpressionsTree } from '../../../data-operations/filtering-expressions-tree';
4+
import { getUUID } from '../../common/random';
45

56
/**
67
* @hidden @internal
@@ -30,7 +31,7 @@ export class ExpressionUI {
3031

3132
constructor() {
3233
// Use IDs to identify expressions clearly and use to track them in template @for cycles.
33-
this.expressionId = crypto.randomUUID();
34+
this.expressionId = getUUID();
3435
}
3536
}
3637

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

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -179,6 +179,7 @@ import { IgxGridCellComponent } from './cell.component';
179179
import { IgxGridValidationService } from './grid/grid-validation.service';
180180
import { getCurrentResourceStrings } from '../core/i18n/resources';
181181
import { isTree, recreateTreeFromFields } from '../data-operations/expressions-tree-util';
182+
import { getUUID } from './common/random';
182183

183184
interface IMatchInfoCache {
184185
row: any;
@@ -3785,7 +3786,7 @@ export abstract class IgxGridBaseDirective implements GridType,
37853786
const primaryColumn = this._columns.find(col => col.field === this.primaryKey);
37863787
const idType = this.data.length ?
37873788
this.resolveDataTypes(this.data[0][this.primaryKey]) : primaryColumn ? primaryColumn.dataType : 'string';
3788-
return idType === 'string' ? crypto.randomUUID() : FAKE_ROW_ID--;
3789+
return idType === 'string' ? getUUID() : FAKE_ROW_ID--;
37893790
}
37903791

37913792
/**

projects/igniteui-angular/src/lib/grids/hierarchical-grid/events.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,4 +7,5 @@ export interface IGridCreatedEventArgs extends IBaseEventArgs {
77
owner: IgxRowIslandComponent;
88
parentID: any;
99
grid: IgxHierarchicalGridComponent;
10+
parentRowData?: any;
1011
}

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

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -214,7 +214,8 @@ export class IgxChildGridRowComponent implements AfterViewInit, OnInit {
214214
this.layout.gridCreated.emit({
215215
owner: this.layout,
216216
parentID: this.data.rowID,
217-
grid: this.hGrid
217+
grid: this.hGrid,
218+
parentRowData: this.data.parentRowData,
218219
});
219220
}
220221

@@ -231,7 +232,8 @@ export class IgxChildGridRowComponent implements AfterViewInit, OnInit {
231232
this.layout.gridInitialized.emit({
232233
owner: this.layout,
233234
parentID: this.data.rowID,
234-
grid: this.hGrid
235+
grid: this.hGrid,
236+
parentRowData: this.data.parentRowData,
235237
});
236238

237239
this.hGrid.cdr.detectChanges();

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@ export class IgxGridHierarchicalPipe implements PipeTransform {
4949
childGridsData[childKey] = childData;
5050
});
5151
if (grid.gridAPI.get_row_expansion_state(v)) {
52-
result.push({ rowID: primaryKey ? v[primaryKey] : v, childGridsData });
52+
result.push({ rowID: primaryKey ? v[primaryKey] : v, childGridsData, parentRowData: v });
5353
}
5454
});
5555
return result;

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

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1119,6 +1119,22 @@ describe('Basic IgxHierarchicalGrid #hGrid', () => {
11191119
expect(getterSpy).toHaveBeenCalledTimes(7);
11201120
expect(summaryCell.textContent.trim()).toEqual('');
11211121
}));
1122+
1123+
it('should verify gridCreated and gridInitialized events emit correct parentRowData', () => {
1124+
const row = hierarchicalGrid.gridAPI.get_row_by_index(0) as IgxHierarchicalRowComponent;
1125+
const rowIsland = fixture.componentInstance.rowIsland1;
1126+
1127+
spyOn(rowIsland.gridCreated, 'emit').and.callThrough();
1128+
spyOn(rowIsland.gridInitialized, 'emit').and.callThrough();
1129+
1130+
UIInteractions.simulateClickAndSelectEvent(row.expander);
1131+
fixture.detectChanges();
1132+
1133+
expect(rowIsland.gridCreated.emit).toHaveBeenCalledTimes(1);
1134+
expect(rowIsland.gridCreated.emit).toHaveBeenCalledWith(jasmine.objectContaining({ parentRowData: row.data }));
1135+
expect(rowIsland.gridInitialized.emit).toHaveBeenCalledTimes(1);
1136+
expect(rowIsland.gridInitialized.emit).toHaveBeenCalledWith(jasmine.objectContaining({ parentRowData: row.data }));
1137+
});
11221138
});
11231139

11241140
describe('IgxHierarchicalGrid Children Sizing #hGrid', () => {

0 commit comments

Comments
 (0)