Skip to content

Commit 724248f

Browse files
committed
Merge branch 'mvenkov/do-not-move-overlay-element-from-its-position' of https://github.com/IgniteUI/igniteui-angular into mvenkov/do-not-move-overlay-element-from-its-position
2 parents 1536152 + eef9610 commit 724248f

File tree

28 files changed

+402
-312
lines changed

28 files changed

+402
-312
lines changed

projects/igniteui-angular/core/src/services/overlay/overlay.spec.ts

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1451,6 +1451,44 @@ describe('igxOverlay', () => {
14511451
// expect(mockElement.style.height).toBe('100px');
14521452
// });
14531453

1454+
it('#16988 - should not reposition overlay when detached', fakeAsync(() => {
1455+
const fixture = TestBed.createComponent(EmptyPageComponent);
1456+
fixture.debugElement.nativeElement.appendChild(outlet);
1457+
outlet.style.width = '800px';
1458+
outlet.style.height = '600px';
1459+
outlet.style.position = 'fixed';
1460+
outlet.style.top = '100px';
1461+
outlet.style.left = '200px';
1462+
outlet.style.overflow = 'hidden';
1463+
fixture.detectChanges();
1464+
1465+
const positionStrategy = new ContainerPositionStrategy();
1466+
const overlaySettings: OverlaySettings = {
1467+
outlet,
1468+
positionStrategy
1469+
};
1470+
1471+
const id = fixture.componentInstance.overlay.attach(SimpleDynamicComponent, overlaySettings);
1472+
fixture.componentInstance.overlay.show(id);
1473+
tick();
1474+
1475+
// Capture the content element while it is still in the DOM
1476+
const contentElement = fixture.nativeElement.parentElement.getElementsByClassName(CLASS_OVERLAY_CONTENT_MODAL)[0] as HTMLElement;
1477+
1478+
// Detaching the overlay calls dispose() on the position strategy which disconnects
1479+
// the IntersectionObserver and sets it to null. However, if a callback was already
1480+
// queued by the observer before disconnect, it will still fire and call updatePosition
1481+
// with the now-detached contentElement (parentElement === null).
1482+
fixture.componentInstance.overlay.detach(id);
1483+
tick();
1484+
1485+
// Simulate the stale IntersectionObserver callback firing after detach.
1486+
// Should not throw even though contentElement is no longer attached to the DOM.
1487+
expect(() => (positionStrategy as any).updatePosition(contentElement)).not.toThrow();
1488+
1489+
fixture.componentInstance.overlay.detachAll();
1490+
}));
1491+
14541492
it('should close overlay on outside click when target is point, #8297', fakeAsync(() => {
14551493
const fixture = TestBed.createComponent(EmptyPageComponent);
14561494
const overlay = fixture.componentInstance.overlay;

projects/igniteui-angular/core/src/services/overlay/position/container-position-strategy.ts

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -42,11 +42,15 @@ export class ContainerPositionStrategy extends GlobalPositionStrategy {
4242
}
4343

4444
private updatePosition(contentElement: HTMLElement): void {
45+
const outletElement = contentElement.parentElement?.parentElement;
46+
if (!outletElement)
47+
return;
48+
4549
// TODO: consider using new anchor() CSS function when it becomes more widely supported: https://caniuse.com/mdn-css_properties_anchor
46-
const parentRect = contentElement.parentElement.parentElement.getBoundingClientRect();
47-
contentElement.parentElement.style.width = `${parentRect.width}px`;
48-
contentElement.parentElement.style.height = `${parentRect.height}px`;
49-
contentElement.parentElement.style.top = `${parentRect.top}px`;
50-
contentElement.parentElement.style.left = `${parentRect.left}px`;
50+
const outletRect = outletElement.getBoundingClientRect();
51+
contentElement.parentElement.style.width = `${outletRect.width}px`;
52+
contentElement.parentElement.style.height = `${outletRect.height}px`;
53+
contentElement.parentElement.style.top = `${outletRect.top}px`;
54+
contentElement.parentElement.style.left = `${outletRect.left}px`;
5155
}
5256
}

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

Lines changed: 28 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -415,7 +415,7 @@ export class IgxForOfDirective<T, U extends T[] = T[]> extends IgxForOfToken<T,U
415415
}
416416

417417
public isScrollable() {
418-
return this.scrollComponent.size > parseInt(this.igxForContainerSize, 10);
418+
return this.scrollComponent.size > parseFloat(this.igxForContainerSize);
419419
}
420420

421421
protected get embeddedViewNodes() {
@@ -583,8 +583,8 @@ export class IgxForOfDirective<T, U extends T[] = T[]> extends IgxForOfToken<T,U
583583
}
584584
const containerSize = 'igxForContainerSize';
585585
if (containerSize in changes && !changes[containerSize].firstChange && this.igxForOf) {
586-
const prevSize = parseInt(changes[containerSize].previousValue, 10);
587-
const newSize = parseInt(changes[containerSize].currentValue, 10);
586+
const prevSize = parseFloat(changes[containerSize].previousValue);
587+
const newSize = parseFloat(changes[containerSize].currentValue);
588588
this._recalcOnContainerChange({prevSize, newSize});
589589
}
590590
}
@@ -641,7 +641,7 @@ export class IgxForOfDirective<T, U extends T[] = T[]> extends IgxForOfToken<T,U
641641
return false;
642642
}
643643
const originalVirtScrollTop = this._virtScrollPosition;
644-
const containerSize = parseInt(this.igxForContainerSize, 10);
644+
const containerSize = parseFloat(this.igxForContainerSize);
645645
const maxVirtScrollTop = this._virtSize - containerSize;
646646

647647
this._bScrollInternal = true;
@@ -692,7 +692,7 @@ export class IgxForOfDirective<T, U extends T[] = T[]> extends IgxForOfToken<T,U
692692
if (index < 0 || index > (this.isRemote ? this.totalItemCount : this.igxForOf.length) - 1) {
693693
return;
694694
}
695-
const containerSize = parseInt(this.igxForContainerSize, 10);
695+
const containerSize = parseFloat(this.igxForContainerSize);
696696
const isPrevItem = index < this.state.startIndex || this.scrollPosition > this.sizesCache[index];
697697
let nextScroll = isPrevItem ? this.sizesCache[index] : this.sizesCache[index + 1] - containerSize;
698698
if (nextScroll < 0) {
@@ -717,7 +717,7 @@ export class IgxForOfDirective<T, U extends T[] = T[]> extends IgxForOfToken<T,U
717717
*/
718718
public scrollNext() {
719719
const scr = Math.abs(Math.ceil(this.scrollPosition));
720-
const endIndex = this.getIndexAt(scr + parseInt(this.igxForContainerSize, 10), this.sizesCache);
720+
const endIndex = this.getIndexAt(scr + parseFloat(this.igxForContainerSize), this.sizesCache);
721721
this.scrollTo(endIndex);
722722
}
723723

@@ -740,7 +740,7 @@ export class IgxForOfDirective<T, U extends T[] = T[]> extends IgxForOfToken<T,U
740740
* ```
741741
*/
742742
public scrollNextPage() {
743-
this.addScroll(parseInt(this.igxForContainerSize, 10));
743+
this.addScroll(parseFloat(this.igxForContainerSize));
744744
}
745745

746746
/**
@@ -751,7 +751,7 @@ export class IgxForOfDirective<T, U extends T[] = T[]> extends IgxForOfToken<T,U
751751
* ```
752752
*/
753753
public scrollPrevPage() {
754-
const containerSize = (parseInt(this.igxForContainerSize, 10));
754+
const containerSize = (parseFloat(this.igxForContainerSize));
755755
this.addScroll(-containerSize);
756756
}
757757

@@ -774,7 +774,7 @@ export class IgxForOfDirective<T, U extends T[] = T[]> extends IgxForOfToken<T,U
774774
// fisrt item is not fully in view
775775
startIndex++;
776776
}
777-
const endIndex = this.getIndexAt(this.scrollPosition + parseInt(this.igxForContainerSize, 10), this.sizesCache);
777+
const endIndex = this.getIndexAt(this.scrollPosition + parseFloat(this.igxForContainerSize), this.sizesCache);
778778
return endIndex - startIndex;
779779
}
780780

@@ -813,7 +813,7 @@ export class IgxForOfDirective<T, U extends T[] = T[]> extends IgxForOfToken<T,U
813813
* ```
814814
*/
815815
public getScrollForIndex(index: number, bottom?: boolean) {
816-
const containerSize = parseInt(this.igxForContainerSize, 10);
816+
const containerSize = parseFloat(this.igxForContainerSize);
817817
const scroll = bottom ? Math.max(0, this.sizesCache[index + 1] - containerSize) : this.sizesCache[index];
818818
return scroll;
819819
}
@@ -837,7 +837,7 @@ export class IgxForOfDirective<T, U extends T[] = T[]> extends IgxForOfToken<T,U
837837
const targetNode = index >= this.state.startIndex && index <= this.state.startIndex + this.state.chunkSize ?
838838
this.embeddedViewNodes[index - this.state.startIndex] : null;
839839
const rowHeight = this.getSizeAt(index);
840-
const containerSize = parseInt(this.igxForContainerSize, 10);
840+
const containerSize = parseFloat(this.igxForContainerSize);
841841
const containerOffset = -(this.scrollPosition - this.sizesCache[this.state.startIndex]);
842842
const endTopOffset = targetNode ? targetNode.offsetTop + rowHeight + containerOffset : containerSize + rowHeight;
843843
return !targetNode || targetNode.offsetTop < Math.abs(containerOffset)
@@ -897,7 +897,7 @@ export class IgxForOfDirective<T, U extends T[] = T[]> extends IgxForOfToken<T,U
897897
}
898898
const scrToBottom = this._isScrolledToBottom && !this.dc.instance.notVirtual;
899899
if (scrToBottom && !this._isAtBottomIndex) {
900-
const containerSize = parseInt(this.igxForContainerSize, 10);
900+
const containerSize = parseFloat(this.igxForContainerSize);
901901
const maxVirtScrollTop = this._virtSize - containerSize;
902902
this._bScrollInternal = true;
903903
this._virtScrollPosition = maxVirtScrollTop;
@@ -946,7 +946,7 @@ export class IgxForOfDirective<T, U extends T[] = T[]> extends IgxForOfToken<T,U
946946
*/
947947
protected onScroll(event) {
948948
/* in certain situations this may be called when no scrollbar is visible */
949-
if (!parseInt(this.scrollComponent.nativeElement.style.height, 10)) {
949+
if (!parseFloat(this.scrollComponent.nativeElement.style.height)) {
950950
return;
951951
}
952952
this.scrollComponent.scrollAmount = event.target.scrollTop;
@@ -1153,7 +1153,7 @@ export class IgxForOfDirective<T, U extends T[] = T[]> extends IgxForOfToken<T,U
11531153
protected onHScroll(event) {
11541154
/* in certain situations this may be called when no scrollbar is visible */
11551155
const firstScrollChild = this.scrollComponent.nativeElement.children.item(0) as HTMLElement;
1156-
if (!parseInt(firstScrollChild.style.width, 10)) {
1156+
if (!parseFloat(firstScrollChild.style.width)) {
11571157
return;
11581158
}
11591159
this.scrollComponent.scrollAmount = event.target.scrollLeft;
@@ -1326,7 +1326,7 @@ export class IgxForOfDirective<T, U extends T[] = T[]> extends IgxForOfToken<T,U
13261326
let maxLength = 0;
13271327
const arr = [];
13281328
let sum = 0;
1329-
const availableSize = parseInt(this.igxForContainerSize, 10);
1329+
const availableSize = parseFloat(this.igxForContainerSize);
13301330
if (!availableSize) {
13311331
return 0;
13321332
}
@@ -1356,7 +1356,7 @@ export class IgxForOfDirective<T, U extends T[] = T[]> extends IgxForOfToken<T,U
13561356
const prevItem = this.igxForOf[prevIndex];
13571357
const prevSize = dimension === 'height' ?
13581358
this.individualSizeCache[prevIndex] :
1359-
parseInt(prevItem[dimension], 10);
1359+
parseFloat(prevItem[dimension]);
13601360
sum = arr.reduce(reducer, prevSize);
13611361
arr.unshift(prevItem);
13621362
length = arr.length;
@@ -1403,19 +1403,19 @@ export class IgxForOfDirective<T, U extends T[] = T[]> extends IgxForOfToken<T,U
14031403
this.dc.instance.notVirtual = !(this.igxForContainerSize && this.dc && this.state.chunkSize < count);
14041404
const scrollable = containerSizeInfo ? this.scrollComponent.size > containerSizeInfo.prevSize : this.isScrollable();
14051405
if (this.igxForScrollOrientation === 'horizontal') {
1406-
const totalWidth = parseInt(this.igxForContainerSize, 10) > 0 ? this._calcSize() : 0;
1407-
if (totalWidth <= parseInt(this.igxForContainerSize, 10)) {
1406+
const totalWidth = parseFloat(this.igxForContainerSize) > 0 ? this._calcSize() : 0;
1407+
if (totalWidth <= parseFloat(this.igxForContainerSize)) {
14081408
this.resetScrollPosition();
14091409
}
14101410
this.scrollComponent.nativeElement.style.width = this.igxForContainerSize + 'px';
14111411
this.scrollComponent.size = totalWidth;
14121412
}
14131413
if (this.igxForScrollOrientation === 'vertical') {
14141414
const totalHeight = this._calcSize();
1415-
if (totalHeight <= parseInt(this.igxForContainerSize, 10)) {
1415+
if (totalHeight <= parseFloat(this.igxForContainerSize)) {
14161416
this.resetScrollPosition();
14171417
}
1418-
this.scrollComponent.nativeElement.style.height = parseInt(this.igxForContainerSize, 10) + 'px';
1418+
this.scrollComponent.nativeElement.style.height = parseFloat(this.igxForContainerSize) + 'px';
14191419
this.scrollComponent.size = totalHeight;
14201420
}
14211421
if (scrollable !== this.isScrollable()) {
@@ -1512,7 +1512,7 @@ export class IgxForOfDirective<T, U extends T[] = T[]> extends IgxForOfToken<T,U
15121512
}
15131513

15141514
protected _calcVirtualScrollPosition(scrollPosition: number) {
1515-
const containerSize = parseInt(this.igxForContainerSize, 10);
1515+
const containerSize = parseFloat(this.igxForContainerSize);
15161516
const maxRealScrollPosition = this.scrollComponent.size - containerSize;
15171517
const realPercentScrolled = maxRealScrollPosition !== 0 ? scrollPosition / maxRealScrollPosition : 0;
15181518
const maxVirtScroll = this._virtSize - containerSize;
@@ -1521,7 +1521,7 @@ export class IgxForOfDirective<T, U extends T[] = T[]> extends IgxForOfToken<T,U
15211521

15221522
protected _getItemSize(item, dimension: string): number {
15231523
const dim = item ? item[dimension] : null;
1524-
return typeof dim === 'number' ? dim : parseInt(this.igxForItemSize, 10) || 0;
1524+
return typeof dim === 'number' ? dim : parseFloat(this.igxForItemSize) || 0;
15251525
}
15261526

15271527
protected _updateScrollOffset() {
@@ -1545,7 +1545,7 @@ export class IgxForOfDirective<T, U extends T[] = T[]> extends IgxForOfToken<T,U
15451545
// should update scroll top/left according to change so that same startIndex is in view
15461546
if (Math.abs(sizeDiff) > 0 && this.scrollPosition > 0) {
15471547
const offset = this.igxForScrollOrientation === 'horizontal' ?
1548-
parseInt(this.dc.instance._viewContainer.element.nativeElement.style.left, 10) :
1548+
parseFloat(this.dc.instance._viewContainer.element.nativeElement.style.left) :
15491549
Number(this.dc.instance._viewContainer.element.nativeElement.style.transform?.match(/translateY\((-?\d+\.?\d*)px\)/)?.[1]);
15501550
const newSize = this.sizesCache[this.state.startIndex] - offset;
15511551
this.scrollPosition = newSize;
@@ -1706,8 +1706,8 @@ export class IgxGridForOfDirective<T, U extends T[] = T[]> extends IgxForOfDirec
17061706
}
17071707
const containerSize = 'igxForContainerSize';
17081708
if (containerSize in changes && !changes[containerSize].firstChange && this.igxForOf) {
1709-
const prevSize = parseInt(changes[containerSize].previousValue, 10);
1710-
const newSize = parseInt(changes[containerSize].currentValue, 10);
1709+
const prevSize = parseFloat(changes[containerSize].previousValue);
1710+
const newSize = parseFloat(changes[containerSize].currentValue);
17111711
this._recalcOnContainerChange({prevSize, newSize});
17121712
}
17131713
}
@@ -1781,7 +1781,7 @@ export class IgxGridForOfDirective<T, U extends T[] = T[]> extends IgxForOfDirec
17811781
public override onHScroll(scrollAmount) {
17821782
/* in certain situations this may be called when no scrollbar is visible */
17831783
const firstScrollChild = this.scrollComponent.nativeElement.children.item(0) as HTMLElement;
1784-
if (!this.scrollComponent || !parseInt(firstScrollChild.style.width, 10)) {
1784+
if (!this.scrollComponent || !parseFloat(firstScrollChild.style.width)) {
17851785
return;
17861786
}
17871787
this.scrollComponent.scrollAmount = scrollAmount;
@@ -1807,7 +1807,7 @@ export class IgxGridForOfDirective<T, U extends T[] = T[]> extends IgxForOfDirec
18071807
size = item.height;
18081808
}
18091809
} else {
1810-
size = parseInt(item[dimension], 10) || 0;
1810+
size = parseFloat(item[dimension]) || 0;
18111811
}
18121812
return size;
18131813
}
@@ -1836,7 +1836,7 @@ export class IgxGridForOfDirective<T, U extends T[] = T[]> extends IgxForOfDirec
18361836
protected override getNodeSize(rNode: Element, index?: number): number {
18371837
if (this.igxForScrollOrientation === 'vertical') {
18381838
const view = this._embeddedViews[index];
1839-
return this._embeddedViewSizesCache.get(view) || parseInt(this.igxForItemSize, 10);
1839+
return this._embeddedViewSizesCache.get(view) || parseFloat(this.igxForItemSize);
18401840
} else {
18411841
return super.getNodeSize(rNode, index);
18421842
}

projects/igniteui-angular/grids/core/src/grid-mrl-navigation.service.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -221,6 +221,9 @@ export class IgxGridMRLNavigationService extends IgxGridNavigationService {
221221
}
222222
const nextLayout = this.layout(colIndex);
223223
const newLayout = key.includes('up') || key.includes('down') ? {rowStart: nextLayout.rowStart} : {colStart: nextLayout.colStart};
224+
if (!this.activeNode.layout) {
225+
this.activeNode.layout = this.layout(this.activeNode.column || 0);
226+
}
224227
Object.assign(this.activeNode.layout, newLayout, {rowEnd: nextLayout.rowEnd});
225228

226229
if (ctrl && (key === 'home' || key === 'end')) {

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

Lines changed: 10 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -3328,6 +3328,7 @@ export abstract class IgxGridBaseDirective implements GridType,
33283328
private _sortDescendingHeaderIconTemplate: TemplateRef<IgxGridHeaderTemplateContext> = null;
33293329
private _gridSize: ɵSize = ɵSize.Large;
33303330
private _defaultRowHeight = 50;
3331+
private _borderSize = 1;
33313332
private _rowCount: number;
33323333
private _cellMergeMode: GridCellMergeMode = GridCellMergeMode.onSort;
33333334
private _columnsToMerge: IgxColumnComponent[] = [];
@@ -5606,7 +5607,7 @@ export abstract class IgxGridBaseDirective implements GridType,
56065607
if (this.hasCellsToMerge) {
56075608
return this.rowHeight;
56085609
}
5609-
return this.rowHeight + 1;
5610+
return this.rowHeight + this._borderSize;
56105611
}
56115612

56125613
/**
@@ -7835,12 +7836,7 @@ export abstract class IgxGridBaseDirective implements GridType,
78357836
}
78367837

78377838
protected get renderedActualRowHeight() {
7838-
let border = 1;
7839-
if (this.rowList.toArray().length > 0) {
7840-
const rowStyles = this.document.defaultView.getComputedStyle(this.rowList.first.nativeElement);
7841-
border = rowStyles.borderBottomWidth ? Math.ceil(parseFloat(rowStyles.borderBottomWidth)) : border;
7842-
}
7843-
return this.rowHeight + border;
7839+
return this.rowHeight + this._borderSize;
78447840
}
78457841

78467842
private executeCallback(rowIndex, visibleColIndex = -1, cb: (args: any) => void = null) {
@@ -8119,6 +8115,13 @@ export abstract class IgxGridBaseDirective implements GridType,
81198115
} else {
81208116
this._shouldRecalcRowHeight = true;
81218117
}
8118+
8119+
const rowStyles = this.document.defaultView.getComputedStyle(this.dataRowList.first.nativeElement);
8120+
8121+
const border = rowStyles.borderBottomWidth ? parseFloat(rowStyles.borderBottomWidth) : 1;
8122+
if (border) {
8123+
this._borderSize = border;
8124+
}
81228125
}
81238126
}
81248127

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -109,6 +109,7 @@ export interface IGroupingDoneEventArgs extends IBaseEventArgs {
109109
preserveWhitespaces: false,
110110
providers: [
111111
IgxGridCRUDService,
112+
IgxGridMRLNavigationService,
112113
IgxGridNavigationService,
113114
IgxGridSummaryService,
114115
IgxGridSelectionService,

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

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -943,7 +943,7 @@ describe('IgxGrid - multi-row-layout #grid', () => {
943943

944944
// check group size is correct
945945
expect(horizontalVirtualization.getSizeAt(0)).toBe(700);
946-
expect(horizontalVirtualization.getSizeAt(1)).toBe(300);
946+
expect(horizontalVirtualization.getSizeAt(1)).toBe(grid.calcWidth * 0.5);
947947

948948
// check DOM
949949
gridFirstRow = grid.rowList.first;
@@ -971,7 +971,7 @@ describe('IgxGrid - multi-row-layout #grid', () => {
971971

972972
// check group size is correct
973973
expect(horizontalVirtualization.getSizeAt(0)).toBe(700);
974-
expect(horizontalVirtualization.getSizeAt(1)).toBe(300);
974+
expect(horizontalVirtualization.getSizeAt(1)).toBe(grid.calcWidth * 0.5);
975975
expect(horizontalVirtualization.getSizeAt(2)).toBe(136 * 4);
976976

977977
// check DOM

0 commit comments

Comments
 (0)