Skip to content

Commit e19bd01

Browse files
authored
Merge branch '21.1.x' into dmdimitrov/export-chat-21.1.x
2 parents ce7b55e + 0277e2c commit e19bd01

File tree

29 files changed

+580
-168
lines changed

29 files changed

+580
-168
lines changed

projects/igniteui-angular/combo/src/combo/combo.component.spec.ts

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1866,6 +1866,40 @@ describe('igxCombo', () => {
18661866
expect(document.activeElement).toEqual(combo.comboInput.nativeElement);
18671867
expect(combo.selection.length).toEqual(0);
18681868
}));
1869+
it('should stop Escape keydown event propagation when the dropdown is open', fakeAsync(() => {
1870+
const escapeEvent = new KeyboardEvent('keydown', { key: 'Escape', bubbles: true });
1871+
spyOn(escapeEvent, 'stopPropagation');
1872+
1873+
combo.comboInput.nativeElement.focus();
1874+
fixture.detectChanges();
1875+
1876+
combo.toggle();
1877+
fixture.detectChanges();
1878+
expect(combo.collapsed).toBeFalsy();
1879+
1880+
combo.onEscape(escapeEvent);
1881+
tick();
1882+
fixture.detectChanges();
1883+
1884+
expect(escapeEvent.stopPropagation).toHaveBeenCalled();
1885+
}));
1886+
it('should stop Escape key propagation when the combo is collapsed and has a selection', fakeAsync(() => {
1887+
combo.comboInput.nativeElement.focus();
1888+
fixture.detectChanges();
1889+
1890+
combo.select([combo.data[0][combo.valueKey]]);
1891+
expect(combo.selection.length).toEqual(1);
1892+
fixture.detectChanges();
1893+
1894+
const keyEvent = new KeyboardEvent('keydown', { key: 'Escape' });
1895+
const stopPropSpy = spyOn(keyEvent, 'stopPropagation');
1896+
1897+
combo.onEscape(keyEvent);
1898+
tick();
1899+
fixture.detectChanges();
1900+
1901+
expect(stopPropSpy).toHaveBeenCalledTimes(1);
1902+
}));
18691903
it('should close the combo and preserve the focus when Escape key is pressed', fakeAsync(() => {
18701904
combo.comboInput.nativeElement.focus();
18711905
fixture.detectChanges();

projects/igniteui-angular/combo/src/combo/combo.component.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -187,6 +187,7 @@ export class IgxComboComponent extends IgxComboBaseDirective implements AfterVie
187187

188188
@HostListener('keydown.Escape', ['$event'])
189189
public onEscape(event: Event) {
190+
event.stopPropagation();
190191
if (this.collapsed) {
191192
this.deselectAllItems(true, event);
192193
}

projects/igniteui-angular/core/src/data-operations/merge-strategy.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -74,7 +74,7 @@ export class DefaultMergeStrategy implements IGridMergeStrategy {
7474
index++;
7575
continue;
7676
}
77-
const recToUpdateData = recData ?? { recordRef: grid.isGhostRecord(rec) ? rec.recordRef : rec, cellMergeMeta: new Map<string, IMergeByResult>(), ghostRecord: rec.ghostRecord };
77+
const recToUpdateData = recData ?? { recordRef: grid.isGhostRecord(rec) ? rec.recordRef : rec, cellMergeMeta: new Map<string, IMergeByResult>(), ghostRecord: rec.ghostRecord, index: index };
7878
recToUpdateData.cellMergeMeta.set(field, { rowSpan: 1, childRecords: [] });
7979
if (prev && comparer.call(this, prev.recordRef, recToUpdateData.recordRef, field, isDate, isTime) && prev.ghostRecord === recToUpdateData.ghostRecord) {
8080
const root = prev.cellMergeMeta.get(field)?.root ?? prev;

projects/igniteui-angular/core/src/grid-column-actions/token.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ export abstract class IgxActionStripToken {
77
public abstract cdr: ChangeDetectorRef
88
public abstract context: any;
99
public abstract menuOverlaySettings: OverlaySettings;
10+
public abstract actionButtons: QueryList<IgxActionStripActionsToken>;
1011
public abstract get hideOnRowLeave(): boolean;
1112

1213
public abstract show(context?: any): void;

projects/igniteui-angular/directives/src/directives/for-of/for_of.directive.ts

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -275,8 +275,11 @@ export class IgxForOfDirective<T, U extends T[] = T[]> extends IgxForOfToken<T,U
275275
protected _differ: IterableDiffer<T> | null = null;
276276
protected _trackByFn: TrackByFunction<T>;
277277
protected individualSizeCache: number[] = [];
278+
/**
279+
* @hidden
280+
*/
278281
/** Internal track for scroll top that is being virtualized */
279-
protected _virtScrollPosition = 0;
282+
public _virtScrollPosition = 0;
280283
/** If the next onScroll event is triggered due to internal setting of scrollTop */
281284
protected _bScrollInternal = false;
282285
// End properties related to virtual height handling
@@ -901,7 +904,7 @@ export class IgxForOfDirective<T, U extends T[] = T[]> extends IgxForOfToken<T,U
901904
const maxVirtScrollTop = this._virtSize - containerSize;
902905
this._bScrollInternal = true;
903906
this._virtScrollPosition = maxVirtScrollTop;
904-
this.scrollPosition = maxVirtScrollTop;
907+
this.scrollPosition = maxVirtScrollTop / this._virtRatio;
905908
return;
906909
}
907910
if (this._adjustToIndex) {
@@ -1529,11 +1532,12 @@ export class IgxForOfDirective<T, U extends T[] = T[]> extends IgxForOfToken<T,U
15291532
let currentScroll = this.scrollPosition;
15301533
if (this._virtRatio !== 1) {
15311534
this._calcVirtualScrollPosition(this.scrollPosition);
1532-
currentScroll = this._virtScrollPosition;
1535+
scrollOffset = this.fixedUpdateAllElements(this._virtScrollPosition);
1536+
} else {
1537+
const scroll = this.scrollComponent.nativeElement;
1538+
scrollOffset = scroll && this.scrollComponent.size ?
1539+
currentScroll - this.sizesCache[this.state.startIndex] : 0;
15331540
}
1534-
const scroll = this.scrollComponent.nativeElement;
1535-
scrollOffset = scroll && this.scrollComponent.size ?
1536-
currentScroll - this.sizesCache[this.state.startIndex] : 0;
15371541
const dir = this.igxForScrollOrientation === 'horizontal' ? 'left' : 'transform';
15381542
this.dc.instance._viewContainer.element.nativeElement.style[dir] = this.igxForScrollOrientation === 'horizontal' ?
15391543
-(scrollOffset) + 'px' :

projects/igniteui-angular/grids/core/src/filtering/excel-style/excel-style-search.component.ts

Lines changed: 30 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -482,17 +482,26 @@ export class IgxExcelStyleSearchComponent implements AfterViewInit, OnDestroy {
482482
const matchedData = cloneHierarchicalArray(this.esf.listData, 'children');
483483
this.displayedListData = this.hierarchicalSelectMatches(matchedData, searchVal);
484484
this.cdr.detectChanges();
485+
/**
486+
* There are two calls of `matchesNumericValue` in this method: one when we generate the displayedListData in hierarchicalSelectMatches method
487+
* and another one when going through the tree nodes. We can avoid the second call by storing the items in a set.
488+
* However, if the datasource is small there is no significant difference in performance but we would be adding extra memory overhead.
489+
* We should test this when https://github.com/IgniteUI/igniteui-angular/issues/17144 issue is fixed with 100k or 1m records
490+
*/
485491
this.tree.nodes.forEach(n => {
486492
n.selected = true;
487-
if ((n.data as FilterListItem).label.toString().toLowerCase().indexOf(searchVal) > -1) {
493+
const item = n.data as FilterListItem;
494+
if (item.label.toString().toLowerCase().indexOf(searchVal) > -1 ||
495+
this.matchesNumericValue(item, searchVal)) {
488496
this.expandAllParentNodes(n);
489497
}
490498
});
491499
} else {
492500
this.displayedListData = this.esf.listData.filter((it, i) => (i === 0 && it.isSpecial) ||
493501
(it.label !== null && it.label !== undefined) &&
494502
!it.isBlanks &&
495-
it.label.toString().toLowerCase().indexOf(searchVal) > -1);
503+
(it.label.toString().toLowerCase().indexOf(searchVal) > -1 ||
504+
this.matchesNumericValue(it, searchVal)));
496505

497506
this.esf.listData.forEach(i => i.isSelected = false);
498507
this.displayedListData.forEach(i => i.isSelected = true);
@@ -724,7 +733,8 @@ export class IgxExcelStyleSearchComponent implements AfterViewInit, OnDestroy {
724733
node.expanded = false;
725734
}
726735

727-
if (element.label.toString().toLowerCase().indexOf(searchVal) > -1) {
736+
if (element.label.toString().toLowerCase().indexOf(searchVal) > -1 ||
737+
this.matchesNumericValue(element, searchVal)) {
728738
element.isSelected = true;
729739
this.hierarchicalSelectAllChildren(element);
730740
this._hierarchicalSelectedItems.push(element);
@@ -802,6 +812,23 @@ export class IgxExcelStyleSearchComponent implements AfterViewInit, OnDestroy {
802812
}
803813
}
804814

815+
private matchesNumericValue(item: FilterListItem, searchVal: string): boolean {
816+
const columnDataType = this.esf.column?.dataType;
817+
if (typeof item.value !== 'number' ||
818+
(columnDataType !== GridColumnDataType.Number &&
819+
columnDataType !== GridColumnDataType.Currency &&
820+
columnDataType !== GridColumnDataType.Percent)) {
821+
return false;
822+
}
823+
824+
let numericValue = item.value;
825+
if (columnDataType === GridColumnDataType.Percent) {
826+
numericValue = parseFloat((item.value * 100).toPrecision(15));
827+
}
828+
829+
return numericValue.toString().toLowerCase().indexOf(searchVal) > -1;
830+
}
831+
805832
private onArrowUpKeyDown() {
806833
if (this.focusedItem && this.focusedItem.index === 0 && this.virtDir.state.startIndex === 0) {
807834
// on ArrowUp the focus stays on the same element if it is the first focused

projects/igniteui-angular/grids/core/src/grid-actions/grid-pinning-actions.component.spec.ts

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,26 @@ describe('igxGridPinningActions #grid ', () => {
7171
const secondToLastVisible = grid.rowList.toArray()[grid.rowList.length - 2];
7272
expect(secondToLastVisible.key).toEqual('FAMIA');
7373
});
74+
75+
it('should not hide action strip in base mode when scrollToRow is invoked', () => {
76+
grid.pinRow('FAMIA');
77+
fixture.detectChanges();
78+
79+
const pinnedRow = grid.pinnedRows[0];
80+
actionStrip.show(pinnedRow);
81+
fixture.detectChanges();
82+
83+
const pinningActions = fixture.debugElement.query(By.directive(IgxGridPinningActionsComponent))
84+
.componentInstance as IgxGridPinningActionsComponent;
85+
spyOn<any>(grid, 'scrollTo');
86+
87+
pinningActions.scrollToRow(null);
88+
fixture.detectChanges();
89+
90+
expect((grid as any).scrollTo).toHaveBeenCalledWith(pinnedRow.data, 0);
91+
expect(actionStrip.hidden).toBeFalse();
92+
expect(actionStrip.context).toBe(pinnedRow);
93+
});
7494
});
7595

7696
describe('Menu ', () => {

projects/igniteui-angular/grids/core/src/grid-actions/grid-pinning-actions.component.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -126,7 +126,10 @@ export class IgxGridPinningActionsComponent extends IgxGridActionsBaseDirective
126126
const context = this.strip.context;
127127
const grid = context.grid;
128128
grid.scrollTo(context.data, 0);
129-
this.strip.hide();
129+
130+
if (this.asMenuItems) {
131+
this.strip.hide();
132+
}
130133
}
131134

132135
private registerSVGIcons(): void {

projects/igniteui-angular/grids/grid/src/cell-merge.spec.ts

Lines changed: 63 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ import { IgxPaginatorComponent } from 'igniteui-angular/paginator';;
66
import { DataParent } from '../../../test-utils/sample-test-data.spec';
77
import { GridFunctions, GridSelectionFunctions } from '../../../test-utils/grid-functions.spec';
88
import { By } from '@angular/platform-browser';
9-
import { UIInteractions, wait } from '../../../test-utils/ui-interactions.spec';
9+
import { UIInteractions, wait, waitForActiveNodeChange } from '../../../test-utils/ui-interactions.spec';
1010
import { hasClass, setElementSize } from '../../../test-utils/helper-utils.spec';
1111
import { ColumnLayoutTestComponent } from './grid.multi-row-layout.spec';
1212
import { IgxHierarchicalGridTestBaseComponent } from '../../hierarchical-grid/src/hierarchical-grid.spec';
@@ -1113,6 +1113,68 @@ describe('IgxGrid - Cell merging #grid', () => {
11131113
]);
11141114
});
11151115

1116+
it('should remerge child grid cells when focus moves to parent grid.', async () => {
1117+
const ri = fix.componentInstance.rowIsland;
1118+
ri.cellMergeMode = 'always';
1119+
ri.getColumnByName('ProductName').merge = true;
1120+
fix.detectChanges();
1121+
1122+
const firstRow = grid.gridAPI.get_row_by_index(0) as IgxHierarchicalRowComponent;
1123+
firstRow.toggle();
1124+
fix.detectChanges();
1125+
1126+
const childGrid = grid.gridAPI.getChildGrids(false)[0] as IgxHierarchicalGridComponent;
1127+
expect(childGrid).toBeDefined();
1128+
1129+
const childCol = childGrid.getColumnByName('ProductName');
1130+
GridFunctions.verifyColumnMergedState(childGrid, childCol, [
1131+
{ value: 'Product A', span: 2 },
1132+
{ value: 'Product B', span: 1 },
1133+
{ value: 'Product A', span: 1 }
1134+
]);
1135+
1136+
await wait(1);
1137+
fix.detectChanges();
1138+
1139+
const allGrids = fix.debugElement.queryAll(By.directive(IgxHierarchicalGridComponent));
1140+
const childGridDE = allGrids.find(x => x.componentInstance === childGrid);
1141+
expect(childGridDE).toBeDefined();
1142+
const childRows = childGridDE.queryAll(By.css(CSS_CLASS_GRID_ROW));
1143+
childRows.shift();
1144+
const childRowDE = childRows[0];
1145+
const childCells = childRowDE.queryAll(By.css('.igx-grid__td'));
1146+
const childCellDE = childCells[1];
1147+
UIInteractions.simulateClickAndSelectEvent(childCellDE.nativeElement);
1148+
await wait(1);
1149+
fix.detectChanges();
1150+
1151+
GridFunctions.verifyColumnMergedState(childGrid, childCol, [
1152+
{ value: 'Product A', span: 1 },
1153+
{ value: 'Product A', span: 1 },
1154+
{ value: 'Product B', span: 1 },
1155+
{ value: 'Product A', span: 1 }
1156+
]);
1157+
1158+
const rootGridDE = allGrids.find(x => x.componentInstance === grid);
1159+
expect(rootGridDE).toBeDefined();
1160+
const parentRows = rootGridDE.queryAll(By.css(CSS_CLASS_GRID_ROW));
1161+
parentRows.shift();
1162+
const parentRowDE = parentRows[0];
1163+
const parentCells = parentRowDE.queryAll(By.css('.igx-grid__td'));
1164+
const parentCellDE = parentCells[1];
1165+
const activeChange = waitForActiveNodeChange(childGrid);
1166+
UIInteractions.simulateClickAndSelectEvent(parentCellDE.nativeElement);
1167+
await activeChange;
1168+
await wait(20);
1169+
fix.detectChanges();
1170+
1171+
GridFunctions.verifyColumnMergedState(childGrid, childCol, [
1172+
{ value: 'Product A', span: 2 },
1173+
{ value: 'Product B', span: 1 },
1174+
{ value: 'Product A', span: 1 }
1175+
]);
1176+
});
1177+
11161178
});
11171179

11181180
describe('TreeGrid', () => {

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

Lines changed: 32 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,8 @@ import { TestBed, fakeAsync, tick, waitForAsync } from '@angular/core/testing';
44
import { DebugElement } from '@angular/core';
55
import { GridFunctions, GridSummaryFunctions } from '../../../test-utils/grid-functions.spec';
66
import {
7-
IgxAddRowComponent, IgxGridRowEditingDefinedColumnsComponent, IgxGridRowEditingTransactionComponent
7+
IgxAddRowComponent, IgxGridRowEditingDefinedColumnsComponent, IgxGridRowEditingTransactionComponent,
8+
GridDynamicActionStripComponent
89
} from '../../../test-utils/grid-samples.spec';
910

1011
import { By } from '@angular/platform-browser';
@@ -45,7 +46,8 @@ describe('IgxGrid - Row Adding #grid', () => {
4546
IgxGridRowEditingTransactionComponent,
4647
IgxGridRowEditingDefinedColumnsComponent,
4748
ColumnLayoutTestComponent,
48-
DefaultGridMasterDetailComponent
49+
DefaultGridMasterDetailComponent,
50+
GridDynamicActionStripComponent
4951
],
5052
providers: [
5153
IgxGridMRLNavigationService
@@ -1121,4 +1123,32 @@ describe('IgxGrid - Row Adding #grid', () => {
11211123
expect(grid.rowChangesCount).toEqual(3);
11221124
});
11231125
});
1126+
1127+
describe('ActionStrip - Dynamic Addition', () => {
1128+
beforeEach(() => {
1129+
fixture = TestBed.createComponent(GridDynamicActionStripComponent);
1130+
fixture.detectChanges();
1131+
grid = fixture.componentInstance.grid;
1132+
});
1133+
1134+
it('Should set outlet for actionstrip menu when added post-init', async () => {
1135+
// Verify no actionstrip initially
1136+
expect(fixture.componentInstance.actionStrip).toBeUndefined();
1137+
expect(grid.actionStrip).toBeUndefined();
1138+
1139+
// Add the actionstrip dynamically
1140+
fixture.componentInstance.showActionStrip = true;
1141+
fixture.detectChanges();
1142+
await wait(16);
1143+
1144+
// Get reference to the actionstrip
1145+
actionStrip = fixture.componentInstance.actionStrip;
1146+
expect(actionStrip).toBeDefined();
1147+
expect(grid.actionStrip).toBeDefined();
1148+
1149+
// Verify that the outlet is properly set
1150+
expect(actionStrip.menuOverlaySettings.outlet).toBeDefined();
1151+
expect(actionStrip.menuOverlaySettings.outlet).toBe(grid.outlet);
1152+
});
1153+
});
11241154
});

0 commit comments

Comments
 (0)