Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
42 commits
Select commit Hold shift + click to select a range
6a757f9
feat(overlay): use popover in overlay service
wnvko Jan 30, 2026
7b4cac3
test(combo): fix combo issue and failing tests
wnvko Feb 3, 2026
851912e
fix(overlay): fix container position strategy
wnvko Feb 3, 2026
532ddca
test(overlay): fix failing tests
wnvko Feb 3, 2026
0788eaf
Merge branch 'master' into mvenkov/use-popover-in-overlay
wnvko Feb 3, 2026
6124cec
chore(overlay): update change log and read me
wnvko Feb 3, 2026
d09d90d
chore(overlay): simplify null check
wnvko Feb 10, 2026
6b1ef0a
fix(overlay): force overlay to initially show and revert combo changes
wnvko Feb 11, 2026
132920e
Merge remote-tracking branch 'remotes/origin/master' into mvenkov/use…
wnvko Feb 11, 2026
d09b208
test(overlay): should retain position in container when container get…
wnvko Feb 11, 2026
9900333
fix(overlay): ensure container resize does not break position and dis…
wnvko Feb 11, 2026
bae50de
fix(overlay): use build in resizeObservable
wnvko Feb 12, 2026
323daaf
chore(*): fix CHANGELOG.md
wnvko Feb 12, 2026
04f1e87
chore(overlay): add TODO for anchor() CSS function
wnvko Feb 12, 2026
b606194
Merge branch 'master' into mvenkov/use-popover-in-overlay
gedinakova Feb 12, 2026
7b06e35
Merge branch 'master' into mvenkov/use-popover-in-overlay
gedinakova Feb 13, 2026
5798a10
fix(overlay): use IntersectionObserver to position the overlay conten…
wnvko Feb 13, 2026
1dcc482
Merge branch 'master' into mvenkov/use-popover-in-overlay
damyanpetev Feb 13, 2026
277606f
fix(overlay): use IntersectionObserver - WIP
wnvko Feb 13, 2026
5d7e774
Merge branch 'mvenkov/use-popover-in-overlay' of https://github.com/I…
wnvko Feb 16, 2026
b2ae512
fix(overlay): reposition overlay when target moves in RowEditPosition…
wnvko Feb 16, 2026
7ad891d
chore(overlay): create IntersectionObserverHelper in overlay Utils
wnvko Feb 16, 2026
a51d305
fix(overlay): use IntersectionObserverHelper in container position st…
wnvko Feb 16, 2026
4f2e84d
test(overlay): check observerHelper and not _resizeSubscription
wnvko Feb 16, 2026
8e516d9
Merge branch 'master' into mvenkov/use-popover-in-overlay
gedinakova Feb 17, 2026
f623e20
Merge branch 'master' into mvenkov/use-popover-in-overlay
gedinakova Feb 17, 2026
c4c18c3
fix(overlay): check if IntersectionObserver exists
wnvko Feb 17, 2026
71d17b4
Merge branch 'master' into mvenkov/use-popover-in-overlay
gedinakova Feb 18, 2026
7b53af6
fix(overlay): fix the AI fix - start using the observer and stop RAF …
wnvko Feb 18, 2026
c51692c
Merge branch 'mvenkov/use-popover-in-overlay' of https://github.com/I…
wnvko Feb 18, 2026
ae17bf6
chore(overlay): update overlay sample to test observer helper
wnvko Feb 18, 2026
3317fc2
Update projects/igniteui-angular/core/src/services/overlay/overlay.sp…
wnvko Feb 18, 2026
4ea4308
Update projects/igniteui-angular/core/src/services/overlay/overlay.sp…
wnvko Feb 18, 2026
5d24d46
Merge branch 'master' into mvenkov/use-popover-in-overlay
gedinakova Feb 18, 2026
7c348dc
fix(overlay): move setupIntersectionObserver as static method under Util
wnvko Feb 18, 2026
b2fde44
Merge branch 'mvenkov/use-popover-in-overlay' of https://github.com/I…
wnvko Feb 18, 2026
d5dd2b2
fix(overlay): simplify intersection observer
wnvko Feb 19, 2026
c8636e1
Merge branch 'master' into mvenkov/use-popover-in-overlay
gedinakova Feb 19, 2026
efdf5c4
Update projects/igniteui-angular/core/src/services/overlay/utilities.ts
wnvko Feb 19, 2026
c72074b
chore(grid): remove row edit position overlay dispose
wnvko Feb 19, 2026
44b3eee
Merge branch 'mvenkov/use-popover-in-overlay' of https://github.com/I…
wnvko Feb 19, 2026
0577064
chore(grid): refactor and refactor
wnvko Feb 19, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,11 @@ All notable changes for each version of this project will be documented in this
```
- `IgxNavigationDrawer` - Integrated HTML Popover API to place overlay elements when not pinned in the top layer, eliminating z-index stacking issues.

- `IgxOverlayService`
- Integrated HTML Popover API into the overlay service for improved z-index management and layering control.
- The overlay service now uses the Popover API to place overlay elements in the top layer, eliminating z-index stacking issues.
- Improved positioning accuracy for container-based overlays with fixed container bounds.

### General

- `IgxGrid`, `IgxTreeGrid`, `IgxHierarchicalGrid`, `IgxPivotGrid`
Expand Down
61 changes: 36 additions & 25 deletions projects/igniteui-angular/combo/src/combo/combo.component.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1016,13 +1016,14 @@ describe('igxCombo', () => {
fixture.detectChanges();
verifyDropdownItemHeight();
}));
it('should render grouped items properly', (done) => {
it('should render grouped items properly', async () => {
let dropdownContainer;
let dropdownItems;
let scrollIndex = 0;
const headers: Array<string> = Array.from(new Set(combo.data.map(item => item.region)));
combo.toggle();
await wait();
fixture.detectChanges();

const checkGroupedItemsClass = () => {
fixture.detectChanges();
dropdownContainer = fixture.debugElement.query(By.css(`.${CSS_CLASS_CONTAINER}`)).nativeElement;
Expand All @@ -1033,18 +1034,18 @@ describe('igxCombo', () => {
const expectedClass: string = headers.includes(itemText) ? CSS_CLASS_HEADERITEM : CSS_CLASS_DROPDOWNLISTITEM;
expect(itemElement.classList.contains(expectedClass)).toBeTruthy();
});
scrollIndex += 10;
if (scrollIndex < combo.data.length) {
combo.virtualScrollContainer.scrollTo(scrollIndex);
combo.virtualScrollContainer.chunkLoad.pipe(take(1)).subscribe(async () => {
await wait(30);
checkGroupedItemsClass();
});
} else {
done();
}
};

// Check initial state
checkGroupedItemsClass();

// Scroll through the list in chunks and verify items
for (let scrollIndex = 10; scrollIndex < combo.data.length; scrollIndex += 10) {
combo.virtualScrollContainer.scrollTo(scrollIndex);
await firstValueFrom(combo.virtualScrollContainer.chunkLoad);
await wait(30);
checkGroupedItemsClass();
}
});
it('should render selected items properly', () => {
combo.toggle();
Expand Down Expand Up @@ -1195,8 +1196,8 @@ describe('igxCombo', () => {
const comboWrapper = fixture.debugElement.query(By.css(CSS_CLASS_COMBO)).nativeElement;
let containerElementWidth = containerElement.getBoundingClientRect().width;
let wrapperWidth = comboWrapper.getBoundingClientRect().width;
expect(containerElementWidth).toEqual(containerWidth);
expect(containerElementWidth).toEqual(wrapperWidth);
expect(containerElementWidth).toBeCloseTo(containerWidth, 1);
expect(containerElementWidth).toBeCloseTo(wrapperWidth, 1);

combo.toggle();
tick();
Expand Down Expand Up @@ -1764,6 +1765,7 @@ describe('igxCombo', () => {
it('should properly navigate using HOME/END key', (async () => {
let firstVisibleItem: Element;
combo.toggle();
await wait();
fixture.detectChanges();
const dropdownContent = fixture.debugElement.query(By.css(`.${CSS_CLASS_CONTENT}`));
const scrollbar = fixture.debugElement.query(By.css(`.${CSS_CLASS_SCROLLBAR_VERTICAL}`)).nativeElement as HTMLElement;
Expand All @@ -1773,7 +1775,7 @@ describe('igxCombo', () => {
await firstValueFrom(combo.virtualScrollContainer.chunkLoad);
fixture.detectChanges();
// Content was scrolled to bottom
expect(scrollbar.scrollHeight - scrollbar.scrollTop).toEqual(scrollbar.clientHeight);
expect(scrollbar.scrollHeight - scrollbar.scrollTop - scrollbar.clientHeight).toBeLessThan(1);

// Scroll to top
UIInteractions.triggerEventHandlerKeyDown('Home', dropdownContent);
Expand All @@ -1782,7 +1784,7 @@ describe('igxCombo', () => {
const dropdownContainer: HTMLElement = fixture.debugElement.query(By.css(`.${CSS_CLASS_CONTAINER}`)).nativeElement;
firstVisibleItem = dropdownContainer.querySelector(`.${CSS_CLASS_DROPDOWNLISTITEM}` + ':first-child');
// Container is scrolled to top
expect(scrollbar.scrollTop).toEqual(32);
expect(scrollbar.scrollTop - 32).toBeLessThan(1);

// First item is focused
expect(firstVisibleItem.classList.contains(CSS_CLASS_FOCUSED)).toBeTruthy();
Expand All @@ -1791,7 +1793,7 @@ describe('igxCombo', () => {
firstVisibleItem = dropdownContainer.querySelector(`.${CSS_CLASS_DROPDOWNLISTITEM}` + ':first-child');

// Scroll has not change
expect(scrollbar.scrollTop).toEqual(32);
expect(scrollbar.scrollTop - 32).toBeLessThan(1);
// First item is no longer focused
expect(firstVisibleItem.classList.contains(CSS_CLASS_FOCUSED)).toBeFalsy();
UIInteractions.triggerEventHandlerKeyDown('Home', dropdownContent);
Expand Down Expand Up @@ -1895,6 +1897,7 @@ describe('igxCombo', () => {
input = fixture.debugElement.query(By.css(`.${CSS_CLASS_INPUTGROUP}`));
let firstVisibleItem: Element;
combo.toggle();
await wait();
fixture.detectChanges();
const dropdownContent = fixture.debugElement.query(By.css(`.${CSS_CLASS_CONTENT}`));
const scrollbar = fixture.debugElement.query(By.css(`.${CSS_CLASS_SCROLLBAR_VERTICAL}`))
Expand All @@ -1905,7 +1908,7 @@ describe('igxCombo', () => {
await firstValueFrom(combo.virtualScrollContainer.chunkLoad);
fixture.detectChanges();
// Content was scrolled to bottom
expect(scrollbar.scrollHeight - scrollbar.scrollTop).toEqual(scrollbar.clientHeight);
expect(scrollbar.scrollHeight - scrollbar.scrollTop - scrollbar.clientHeight).toBeLessThan(1);

// Scroll to top
UIInteractions.triggerEventHandlerKeyDown('Home', dropdownContent);
Expand Down Expand Up @@ -1983,8 +1986,9 @@ describe('igxCombo', () => {
vContainerScrollHeight = virtDir.getScroll().scrollHeight;
expect(virtDir.getScroll().scrollTop).toEqual(vContainerScrollHeight / 2);
});
it('should display vertical scrollbar properly', () => {
it('should display vertical scrollbar properly', async () => {
combo.toggle();
await wait();
fixture.detectChanges();
const scrollbarContainer = fixture.debugElement
.query(By.css(`.${CSS_CLASS_SCROLLBAR_VERTICAL}`))
Expand All @@ -1995,12 +1999,14 @@ describe('igxCombo', () => {
combo.data = [{ field: 'Mid-Atlantic', region: 'New Jersey' }, { field: 'Mid-Atlantic', region: 'New York' }];
fixture.detectChanges();
combo.toggle();
await wait();
fixture.detectChanges();
hasScrollbar = scrollbarContainer.scrollHeight > scrollbarContainer.clientHeight;
expect(hasScrollbar).toBeFalsy();
});
it('should preserve selection on scrolling', async () => {
combo.toggle();
await wait();
fixture.detectChanges();
const scrollbar = fixture.debugElement.query(By.css(`.${CSS_CLASS_SCROLLBAR_VERTICAL}`)).nativeElement as HTMLElement;
expect(scrollbar.scrollTop).toEqual(0);
Expand All @@ -2019,7 +2025,7 @@ describe('igxCombo', () => {
await firstValueFrom(combo.virtualScrollContainer.chunkLoad);
fixture.detectChanges();
// Content was scrolled to bottom
expect(scrollbar.scrollHeight - scrollbar.scrollTop).toEqual(scrollbar.clientHeight);
expect(scrollbar.scrollHeight - scrollbar.scrollTop - scrollbar.clientHeight).toBeLessThan(1);

combo.virtualScrollContainer.scrollTo(4);
await firstValueFrom(combo.virtualScrollContainer.chunkLoad);
Expand Down Expand Up @@ -2577,7 +2583,7 @@ describe('igxCombo', () => {
fixture.detectChanges();
expect(combo.dropdown.headers[0].element.nativeElement.innerText).toEqual('New England')
});
it('should sort groups with diacritics correctly', () => {
it('should sort groups with diacritics correctly', async() => {
combo.data = [
{ field: "Alaska", region: "Méxícó" },
{ field: "California", region: "Méxícó" },
Expand All @@ -2589,6 +2595,7 @@ describe('igxCombo', () => {
];
combo.groupSortingDirection = SortingDirection.Asc;
combo.toggle();
await wait();
fixture.detectChanges();
let headers = combo.dropdown.headers.map(header => header.element.nativeElement.innerText);
expect(headers).toEqual(['Ángel', 'Boris', 'México']);
Expand Down Expand Up @@ -2745,9 +2752,11 @@ describe('igxCombo', () => {
combo.filterFunction = comboIgnoreDiacriticsFilter;
combo.displayKey = null;
combo.valueKey = null;
combo.groupKey = null;
combo.filteringOptions = { caseSensitive: false, filteringKey: undefined };
combo.data = ['José', 'Óscar', 'Ángel', 'Germán', 'Niño', 'México', 'Méxícó', 'Mexico', 'Köln', 'München'];
combo.toggle();
tick();
fixture.detectChanges();

const searchInput = fixture.debugElement.query(By.css(`input[name="searchInput"]`));
Expand All @@ -2762,8 +2771,8 @@ describe('igxCombo', () => {

verifyFilteredItems('jose', 1);
verifyFilteredItems('mexico', 3);
verifyFilteredItems('o', 6);
verifyFilteredItems('é', 6);
verifyFilteredItems('o', 7);
verifyFilteredItems('é', 7);
}));

it('should filter the dropdown items when typing in the search input', fakeAsync(() => {
Expand All @@ -2779,6 +2788,7 @@ describe('igxCombo', () => {
};

combo.toggle();
tick();
fixture.detectChanges();
const searchInput = fixture.debugElement.query(By.css('input[name=\'searchInput\']'));
const verifyFilteredItems = (inputValue: string, expectedItemsNumber) => {
Expand Down Expand Up @@ -2842,10 +2852,11 @@ describe('igxCombo', () => {
verifyOnSearchInputEventIsFired('Miss');
verifyOnSearchInputEventIsFired('Misso');
});
it('should restore the initial combo dropdown list after clearing the search input', () => {
it('should restore the initial combo dropdown list after clearing the search input', fakeAsync(() => {
let dropdownList;
let dropdownItems;
combo.toggle();
tick();
fixture.detectChanges();
const searchInput = fixture.debugElement.query(By.css(CSS_CLASS_SEARCHINPUT));

Expand All @@ -2864,7 +2875,7 @@ describe('igxCombo', () => {
verifyFilteredItems('Mi', 3, 5);
verifyFilteredItems('M', 4, 15);
combo.filteredData.forEach((item) => expect(combo.data).toContain(item));
});
}));
it('should clear the search input and close the dropdown list on pressing ESC key', fakeAsync(() => {
combo.toggle();
fixture.detectChanges();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,23 @@
background: transparent;
transition: background .25s $in-out-quad;
pointer-events: none;
z-index: 10005;
box-sizing: content-box;

// Override browser's default popover styles to maintain our custom positioning
&[popover] {
// Reset popover defaults to use our positioning with !important to override UA styles
position: fixed !important;
margin: 0 !important;
border: 0 !important;
padding: 0 !important;
width: auto;
height: auto;
overflow: visible !important;

&:not(:popover-open) {
display: initial !important;
}
}
}

%overlay-wrapper--modal {
Expand Down Expand Up @@ -79,6 +94,5 @@
pointer-events: none;
overflow: hidden;
appearance: none;
z-index: -1;
}
}
8 changes: 8 additions & 0 deletions projects/igniteui-angular/core/src/services/overlay/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,14 @@
The overlay service allows users to show components on overlay div above all other elements in the page.
A walk through of how to get started can be found [here](https://www.infragistics.com/products/ignite-ui-angular/angular/components/overlay-main)

## Key Features

- **Popover API Integration**: Uses the HTML Popover API for improved z-index management and automatic top-layer placement
- **Flexible Positioning**: Multiple position strategies (global, connected, auto, elastic, container)
- **Scroll Strategies**: Handle scroll behavior with NoOp, Block, Close, and Absolute strategies
- **Modal Support**: Optional modal backdrop with configurable close-on-outside-click behavior
- **Animation Support**: Built-in support for open/close animations

## Usage

### With igxToggleDirective
Expand Down
Loading
Loading