Skip to content

refactor(overlay): stop moving to body container & deprecate outlet#16989

Open
wnvko wants to merge 5 commits intomasterfrom
mvenkov/do-not-move-overlay-element-from-its-position
Open

refactor(overlay): stop moving to body container & deprecate outlet#16989
wnvko wants to merge 5 commits intomasterfrom
mvenkov/do-not-move-overlay-element-from-its-position

Conversation

@wnvko
Copy link
Contributor

@wnvko wnvko commented Mar 5, 2026

Closes #16825
Closes #17041

With this PR we are introducing keepInPlace property in OverlaySettings. When set to true overlay will not move the element to be shown out of its position. Instead the element will be wrapped in a content element and in a wrapper element. The new property default value is false to keep the current behavior.

With this PR we also deprecate the outlet property of OverlaySettings. This is not needed anymore as overlay element could be kept in its original position via the new keepInPlace property.

Next steps:

  1. Remove the outlet.
  2. Make keepInPlace default value true and deprecate it.
  3. Remove keepInPlace and always keep the overlay element at its original position.

Additional information (check all that apply):

  • Bug fix
  • New functionality
  • Documentation
  • Demos
  • CI/CD

Checklist:

  • All relevant tags have been applied to this PR
  • This PR includes unit tests covering all the new code (test guidelines)
  • This PR includes API docs for newly added methods/properties (api docs guidelines)
  • This PR includes feature/README.MD updates for the feature docs
  • This PR includes general feature table updates in the root README.MD
  • This PR includes CHANGELOG.MD updates for newly added functionality
  • This PR contains breaking changes
  • This PR includes ng update migrations for the breaking changes (migrations guidelines)
  • This PR includes behavioral changes and the feature specification has been updated with them

@wnvko wnvko marked this pull request as draft March 5, 2026 08:45
@wnvko wnvko added 🛠️ status: in-development Issues and PRs with active development on them ❌ status: awaiting-test PRs awaiting manual verification and removed 🛠️ status: in-development Issues and PRs with active development on them labels Mar 5, 2026
@wnvko wnvko marked this pull request as ready for review March 5, 2026 09:15
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Introduces an opt-in keepInPlace flag for OverlaySettings to prevent overlay content from being moved to a centralized overlay container, and begins deprecating OverlaySettings.outlet in favor of the new behavior.

Changes:

  • Added keepInPlace?: boolean to OverlaySettings and implemented in-place wrapping/unwrapping logic in IgxOverlayService.
  • Deprecated OverlaySettings.outlet and updated multiple feature READMEs + changelog to document the new option.
  • Updated grid/pivot-grid filtering overlay settings and adjusted dialog/overlay tests to account for in-place wrapping behavior.

Reviewed changes

Copilot reviewed 14 out of 14 changed files in this pull request and generated 7 comments.

Show a summary per file
File Description
projects/igniteui-angular/core/src/services/overlay/utilities.ts Adds keepInPlace to OverlaySettings; marks outlet as deprecated.
projects/igniteui-angular/core/src/services/overlay/overlay.ts Implements in-place wrapping path for overlays + cleanup changes.
projects/igniteui-angular/core/src/services/overlay/overlay.spec.ts Adds unit tests validating keep-in-place wrapping/unwrapping and mixed modes.
projects/igniteui-angular/grids/grid/src/grid-base.directive.ts Sets keepInPlace on grid filter and advanced filtering overlay settings.
projects/igniteui-angular/grids/pivot-grid/src/pivot-grid.component.ts Sets keepInPlace on pivot grid ESF filter overlay settings.
projects/igniteui-angular/directives/src/directives/toggle/toggle.directive.ts Defaults toggle attach to { keepInPlace: true, ...overlaySettings }.
projects/igniteui-angular/dialog/src/dialog/dialog.component.spec.ts Updates assertions to locate wrapper within the component host when in-place wrapping is used.
projects/igniteui-angular/core/src/services/overlay/README.md Documents keepInPlace and deprecates outlet in docs.
projects/igniteui-angular/core/src/services/overlay/position/README.md Notes outlet deprecation in positioning docs.
projects/igniteui-angular/drop-down/src/drop-down/autocomplete/README.md Updates autocomplete overlay settings docs; notes outlet deprecation.
projects/igniteui-angular/directives/src/directives/toggle/README.md Deprecates igxToggleOutlet docs and adds keepInPlace guidance.
projects/igniteui-angular/date-picker/src/date-picker/README.md Marks date-picker outlet as deprecated in docs.
projects/igniteui-angular/date-picker/src/date-range-picker/README.md Marks date-range-picker outlet as deprecated in docs.
CHANGELOG.md Adds an entry for the new keepInPlace feature and outlet deprecation.

@mtsvyatkova mtsvyatkova self-assigned this Mar 5, 2026
@mtsvyatkova mtsvyatkova added 💥 status: in-test PRs currently being tested and removed ❌ status: awaiting-test PRs awaiting manual verification labels Mar 5, 2026
@wnvko wnvko force-pushed the mvenkov/do-not-move-overlay-element-from-its-position branch from 1b73b1b to 73ce1fa Compare March 5, 2026 13:22
simeonoff
simeonoff previously approved these changes Mar 10, 2026
@wnvko wnvko force-pushed the mvenkov/do-not-move-overlay-element-from-its-position branch from 51e13b3 to 62135bb Compare March 12, 2026 10:27
@wnvko wnvko requested review from Copilot and simeonoff March 12, 2026 10:28
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 29 out of 29 changed files in this pull request and generated 26 comments.

@wnvko wnvko force-pushed the mvenkov/do-not-move-overlay-element-from-its-position branch from 32e9669 to 7b59366 Compare March 12, 2026 11:02
@wnvko wnvko force-pushed the mvenkov/do-not-move-overlay-element-from-its-position branch from 7b59366 to 66c36b1 Compare March 12, 2026 11:06
@wnvko wnvko force-pushed the mvenkov/do-not-move-overlay-element-from-its-position branch from 2ded560 to 91d3085 Compare March 13, 2026 12:47
@damyanpetev damyanpetev changed the title Do not move overlay element out of its place refactor(overlay): stop moving to body container & deprecate outlet Mar 13, 2026
@damyanpetev damyanpetev added squash-merge Merge PR with "Squash and Merge" option ❗ behavior-change and removed version: 21.1.x labels Mar 13, 2026
Copy link
Member

@damyanpetev damyanpetev left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Might need to discuss the additional container properties, as I missed how that strategy worked before and those might be redundant, which will shift a few more things around.

}
this.io = Util.setupIntersectionObserver(
outletElement,
target as HTMLElement || outletElement,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Checks above already narrow the type to just that option

Suggested change
target as HTMLElement || outletElement,
target || outletElement,

target as HTMLElement || outletElement,
contentElement.ownerDocument,
() => this.updatePosition(contentElement)
() => this.updatePosition(contentElement, target as HTMLElement)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
() => this.updatePosition(contentElement, target as HTMLElement)
() => this.updatePosition(contentElement, target)

() => this.updatePosition(contentElement, target as HTMLElement)
);
this.internalPosition(contentElement);
this.internalPosition(contentElement, target as HTMLElement);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
this.internalPosition(contentElement, target as HTMLElement);
this.internalPosition(contentElement, target);

Comment on lines +55 to +68
if (outletElement) {
const outletRect = outletElement.getBoundingClientRect();
contentElement.parentElement.style.width = `${outletRect.width}px`;
contentElement.parentElement.style.height = `${outletRect.height}px`;
contentElement.parentElement.style.top = `${outletRect.top}px`;
contentElement.parentElement.style.left = `${outletRect.left}px`;
}
if (targetElement) {
const targetRect = targetElement.getBoundingClientRect();
contentElement.parentElement.style.width = `${targetRect.width}px`;
contentElement.parentElement.style.height = `${targetRect.height}px`;
contentElement.parentElement.style.top = `${targetRect.top}px`;
contentElement.parentElement.style.left = `${targetRect.left}px`;
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh absolutely not - this was a bit much to begin with, duplicating it not helping.
Call it with either/or like this.updatePosition(contentElement ?? targetElement) and redo the flow to fit into a single logic path

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also, from what I see the strategy didn't actually use the outlet property? Merely sized itself on the current DOM placement? We might need to discuss, since that's pretty reasonable and we might not need to do extra work to begin with.

| HorizontalAlignment.Center | VerticalAlignment.Middle |

2) **Container** - Positions the element inside the containing outlet based on the directions passed in trough PositionSettings. These are Top/Middle/Bottom for verticalDirection and Left/Center/Right for horizontalDirection. Defaults to:
2) **Container** - Positions the element inside a target container element. The `target` property in `OverlaySettings` must be set to an `HTMLElement` that serves as the container. The overlay wrapper is sized and positioned to match the container's bounds and automatically updates on resize via `ResizeObserver`. Directions are passed in through PositionSettings (Top/Middle/Bottom for verticalDirection and Left/Center/Right for horizontalDirection). Defaults to:
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Eh keep the slop down :D That info is entirely redundant for external use.

Suggested change
2) **Container** - Positions the element inside a target container element. The `target` property in `OverlaySettings` must be set to an `HTMLElement` that serves as the container. The overlay wrapper is sized and positioned to match the container's bounds and automatically updates on resize via `ResizeObserver`. Directions are passed in through PositionSettings (Top/Middle/Bottom for verticalDirection and Left/Center/Right for horizontalDirection). Defaults to:
2) **Container** - Positions the element inside a target container element. The `target` property in `OverlaySettings` must be set to an `HTMLElement` that serves as the container. Directions are passed in through PositionSettings (Top/Middle/Bottom for verticalDirection and Left/Center/Right for horizontalDirection). Defaults to:

Also, if we keep the target, I fail to see why we can't position against a point if that's what's provided.

@ViewChild('loadingOverlay', { read: IgxToggleDirective, static: true })
public loadingOverlay: IgxToggleDirective;

// outlet - not public - do not deprecate
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, right, also don't leave rando comments in the source for no reason

Suggested change
// outlet - not public - do not deprecate

@ViewChild('tfoot', { static: true })
public tfoot: ElementRef<HTMLElement>;

// outlet - not public - do not deprecate
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
// outlet - not public - do not deprecate

@ContentChildren(IgxPaginatorToken)
protected paginationComponents: QueryList<IgxPaginatorComponent>;

// outlet - not public - do not deprecate
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
// outlet - not public - do not deprecate

</igx-grid-header-row>

<div igxGridBody (keydown.control.c)="copyHandler($event)" (copy)="copyHandler($event)" class="igx-grid__tbody" role="rowgroup">
<div #gridBody igxGridBody (keydown.control.c)="copyHandler($event)" (copy)="copyHandler($event)" class="igx-grid__tbody" role="rowgroup">
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
<div #gridBody igxGridBody (keydown.control.c)="copyHandler($event)" (copy)="copyHandler($event)" class="igx-grid__tbody" role="rowgroup">
<div igxGridBody (keydown.control.c)="copyHandler($event)" (copy)="copyHandler($event)" class="igx-grid__tbody" role="rowgroup">

There are already a dozen places where grid body is used in logic, so there are plenty of selectors for it, prob a viewchild - use what's there already?

Comment on lines +9 to +20
- `IgxOverlayService`
- The overlay service now integrates the HTML Popover API and prefers rendering content in place / in the top layer, significantly reducing the need for outlet containers. When configured, `OverlaySettings.outlet` is still honored, and overlays may also fall back to being appended to `document.body` when required.

- **Deprecation** - The `outlet` property in `OverlaySettings`, `IgxOverlayOutletDirective`, and `igxToggleOutlet` input on `IgxToggleActionDirective` are deprecated and will be removed in a future version. They remain functional in this release for backward compatibility, but new code should rely on the default in-place / top-layer rendering behavior instead of custom outlet containers.

- `ContainerPositionStrategy` - The `ContainerPositionStrategy` now uses the `target` property from `OverlaySettings` (when set to an `HTMLElement`) as the container in which the overlay is rendered. This replaces the previous reliance on the deprecated `outlet` property and internal DOM traversal. The overlay wrapper is sized and positioned to match the target container's bounds and automatically updates on resize via `ResizeObserver`.

- `IgxOverlayService.createAbsoluteOverlaySettings` - Added a new overload accepting `container?: HTMLElement` as the second parameter. When a container element is provided, a `ContainerPositionStrategy` is used and the `target` in the returned overlay settings is set to the container element. The previous overload accepting `outlet?: IgxOverlayOutletDirective | ElementRef` is still supported but deprecated.

- `IgxNotificationsDirective`, `IgxSnackbarComponent`, `IgxToastComponent`
- Added a new `container` input property of type `HTMLElement`. When set, overlay content is rendered inside the given container using `ContainerPositionStrategy`. The deprecated `outlet` property now points users to `container` as its replacement.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Only the new IgxOverlayService.createAbsoluteOverlaySettings can really be considered a new feature and the container prop, though that's repalacing.

The behavior change and deprecation fall under a different section (prob General).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

💥 status: in-test PRs currently being tested ❗ behavior-change overlay squash-merge Merge PR with "Squash and Merge" option

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Row added snack-bar is mispositioned Retain the position in DOM of the element shown in overlay

5 participants