fix(ESF): move custom dialog out of ESF markup#17136
Conversation
| this._overlayService.opening.pipe(takeUntil(this.destroy$)).subscribe((args) => { | ||
| if (args.id === overlayId) { | ||
| customDialog.esf = this.esf; | ||
| customDialog.column = this.esf.column; | ||
| customDialog.filteringService = this.esf.grid.filteringService; | ||
| customDialog.overlayComponentId = overlayId; | ||
| customDialog.selectedOperator = eventArgs.newSelection.value; |
There was a problem hiding this comment.
I don't see a reason for the opening here, can we have mostly the same logic as before that just assigned the customDialog props
| modal: false, | ||
| closeOnOutsideClick: true, | ||
| positionStrategy: new ContainerPositionStrategy(), | ||
| scrollStrategy: new AbsoluteScrollStrategy() |
There was a problem hiding this comment.
This does.. nothing for container strategy?
| const overlayId = this._overlayService.attach(IgxExcelStyleCustomDialogComponent, this.esf.grid.viewRef, overlaySettings); | ||
| const overlayInfo = this._overlayService.getOverlayById(overlayId); | ||
| const customDialog = overlayInfo.componentRef.instance as IgxExcelStyleCustomDialogComponent; | ||
| this.esf.grid.tbody.nativeElement.appendChild(overlayInfo.wrapperElement.parentElement); |
There was a problem hiding this comment.
I know this is needed atm, but we could consider leaving the dialog on the grid container instead as well (separate from the PR);
…hub.com/IgniteUI/igniteui-angular into mvenkov/move-esf-dialog-out-of-dropdown
| if (this.esf.overlayComponentId) { | ||
| this.esf.hide(); | ||
| } | ||
| this.subMenu.close(); | ||
| this.customDialog.open(this.esf.mainDropdown.nativeElement); | ||
| this._overlayService.show(overlayId); |
There was a problem hiding this comment.
I know this PR merely moves things around to remove the outlet usage, but this component can really use some refactoring where the onSubMenuSelection is emitted upwards for the root ESF component to handle, close itself and spawn the dialog. And possibly some actual dialog and/or dialog ARIA would be nice. Just 2c
There was a problem hiding this comment.
Pull request overview
This PR addresses an Excel Style Filtering (ESF) rendering issue where the custom filter dialog could become hidden when its parent ESF component is closed (parent becomes display: none). The fix moves the custom dialog out of the igx-excel-style-conditional-filter markup and shows it via IgxOverlayService.
Changes:
- Remove
<igx-excel-style-custom-dialog>fromexcel-style-conditional-filtertemplate and create/show it dynamically throughIgxOverlayService. - Refactor the custom dialog component to no longer rely on
IgxToggleDirectivefor open/close lifecycle. - Update affected grid filtering UI tests and helper utilities to match the new overlay behavior.
Reviewed changes
Copilot reviewed 6 out of 6 changed files in this pull request and generated 3 comments.
Show a summary per file
| File | Description |
|---|---|
| projects/igniteui-angular/test-utils/grid-functions.spec.ts | Adjusts operator dropdown indexing used by ESF-related test helpers. |
| projects/igniteui-angular/grids/grid/src/grid-filtering-ui.spec.ts | Updates ESF size-related tests to align with the new custom dialog hosting/overlay behavior. |
| projects/igniteui-angular/grids/core/src/filtering/excel-style/excel-style-custom-dialog.component.ts | Removes toggle-based overlay setup and switches to overlay-service-driven close behavior (incl. detach). |
| projects/igniteui-angular/grids/core/src/filtering/excel-style/excel-style-custom-dialog.component.html | Removes igxToggle host/event bindings from the dialog root element. |
| projects/igniteui-angular/grids/core/src/filtering/excel-style/excel-style-conditional-filter.component.ts | Dynamically attaches/initializes the custom dialog via IgxOverlayService instead of using a child component reference. |
| projects/igniteui-angular/grids/core/src/filtering/excel-style/excel-style-conditional-filter.component.html | Removes the embedded custom dialog element from the component template. |
| const overlayId = this._overlayService.attach(IgxExcelStyleCustomDialogComponent, this.esf.grid.viewRef, overlaySettings); | ||
| const overlayInfo = this._overlayService.getOverlayById(overlayId); | ||
| const customDialog = overlayInfo.componentRef.instance as IgxExcelStyleCustomDialogComponent; | ||
| this.esf.grid.tbody.nativeElement.appendChild(overlayInfo.wrapperElement.parentElement); |
There was a problem hiding this comment.
Appending the overlay container (overlayInfo.wrapperElement.parentElement) directly into grid.tbody results in invalid table DOM (a <div> under <tbody>). It also interferes with IgxOverlayService.hide() cleanup (hide does not remove the wrapper/container), so closing on outside click can leave orphaned overlay containers in the table. Prefer providing a proper overlay container via overlaySettings.outlet (e.g. a positioned grid container element) or appending to a non-table container element, and ensure the container is removed on close.
| this.esf.grid.tbody.nativeElement.appendChild(overlayInfo.wrapperElement.parentElement); |
| const overlaySettings: OverlaySettings = { | ||
| modal: false, | ||
| closeOnOutsideClick: true, | ||
| positionStrategy: new ContainerPositionStrategy() | ||
| }; | ||
| const overlayId = this._overlayService.attach(IgxExcelStyleCustomDialogComponent, this.esf.grid.viewRef, overlaySettings); | ||
| const overlayInfo = this._overlayService.getOverlayById(overlayId); | ||
| const customDialog = overlayInfo.componentRef.instance as IgxExcelStyleCustomDialogComponent; | ||
| this.esf.grid.tbody.nativeElement.appendChild(overlayInfo.wrapperElement.parentElement); | ||
|
|
||
| customDialog.esf = this.esf; | ||
| customDialog.column = this.esf.column; | ||
| customDialog.filteringService = this.esf.grid.filteringService; | ||
| customDialog.overlayComponentId = overlayId; | ||
| if (this.esf.expressionsList && this.esf.expressionsList.length && | ||
| this.esf.expressionsList[0].expression.condition.name !== 'in') { | ||
| this.customDialog.expressionsList = this.esf.expressionsList; | ||
| customDialog.expressionsList = this.esf.expressionsList; | ||
| } else { | ||
| this.customDialog.expressionsList = this.customDialog.expressionsList.filter(e => e.expression.fieldName === this.esf.column.field && e.expression.condition); | ||
| customDialog.expressionsList = customDialog.expressionsList.filter(e => e.expression.fieldName === this.esf.column.field && e.expression.condition); | ||
| } | ||
| customDialog.selectedOperator = eventArgs.newSelection.value; | ||
|
|
||
| this._overlayService.opening.pipe(takeUntil(this.destroy$)).subscribe((args) => { | ||
| if (args.id === overlayId) | ||
| customDialog.onCustomDialogOpening(); | ||
| }); | ||
| this._overlayService.opened.pipe(takeUntil(this.destroy$)).subscribe((args) => { | ||
| if (args.id === overlayId) | ||
| customDialog.onCustomDialogOpened(); | ||
| }); |
There was a problem hiding this comment.
onSubMenuSelection creates a new overlay and subscribes to overlayService.opening/opened every time the submenu selection changes, but never detaches the overlay when it is closed via outside click (and doesn’t keep the overlayId to clean it up on destroy). This can accumulate component instances/subscriptions over time. Consider reusing a single attached overlay (cache overlayId/componentRef), and/or subscribe to overlayService.closed for this overlayId (with take(1)) to detach after hide, and detach any remaining overlay in ngOnDestroy.
| const verifyGridSubmenuSize = (gridNativeElement: HTMLElement, expectedSize: ɵSize) => { | ||
| const outlet = gridNativeElement.querySelector('.igx-grid__outlet'); | ||
| const dropdowns = Array.from(outlet.querySelectorAll('.igx-drop-down__list')); | ||
| const visibleDropdown: any = dropdowns.find((d) => !d.classList.contains('igx-toggle--hidden')); | ||
| const visibleDropdown: any = dropdowns[0]; | ||
| const dropdownItems = visibleDropdown.querySelectorAll('igx-drop-down-item'); |
There was a problem hiding this comment.
verifyGridSubmenuSize now unconditionally uses dropdowns[0]. In the scenarios where multiple dropdown lists exist in the grid outlet (e.g. cascade submenu list + expression operator dropdown), the first list can be hidden (igx-toggle--hidden) and not the one currently opened, making the assertions target the wrong dropdown. Restoring the previous logic to pick the visible list (e.g. find the one without igx-toggle--hidden) would make the test robust.
Right now the custom dialog shown in ESF is a child element of the
excel-style-conditional-filtercomponent. When custom dialog is shown its parent is closed. This makes the parent of the dialog invisible, marked withdisplay: none. This effectively hides and the dialog. To fix this custom dialog is now removed fromexcel-style-conditional-filtercomponent. Custom dialog is shown directly viaoverlayservice and is provided as component.Next step will be to remove the grid outlet used to show the custom dialog in the grid as this will not be needed anymore.
Closes #17039
Additional information (check all that apply):
Checklist:
feature/README.MDupdates for the feature docsREADME.MDCHANGELOG.MDupdates for newly added functionalityng updatemigrations for the breaking changes (migrations guidelines)