Skip to content

refactor(ESF, combo): use IgxVirtualScrollComponent instead of igxFor - #17579

Open
viktorkombov wants to merge 8 commits into
rkaraivanov/virtual-scroll-componentfrom
vkombov/use-virtual-scroll-for-combo-esf
Open

refactor(ESF, combo): use IgxVirtualScrollComponent instead of igxFor#17579
viktorkombov wants to merge 8 commits into
rkaraivanov/virtual-scroll-componentfrom
vkombov/use-virtual-scroll-for-combo-esf

Conversation

@viktorkombov

Copy link
Copy Markdown
Contributor

Closes #

Description

Motivation / Context

Type of Change (check all that apply):

  • Bug fix
  • New functionality
  • Breaking change (fix or feature that would cause existing functionality to not work as expected)
  • Refactoring (no functional changes)
  • Documentation
  • Demos
  • CI/CD
  • Tests
  • Changelog
  • Skills/Agents

Component(s) / Area(s) Affected:

How Has This Been Tested?

  • Unit tests
  • Manual testing
  • Automated e2e tests

Test Configuration:

  • Angular version:
  • Browser(s):
  • OS:

Screenshots / Recordings

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
  • Accessibility (ARIA, keyboard navigation, focus management) has been verified

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🟡 Changes recommended

The critical test failures and moderate accessibility and navigation issues must be resolved before approval.

Once you've addressed the issues Copilot identified, you can request another Copilot review.

Pull request overview

Migrates Excel-style filtering from igxFor to IgxVirtualScrollComponent; the titled Combo migration is absent.

Changes:

  • Replaces ESF virtualization and keyboard-navigation logic.
  • Updates virtual-scroll markup and documentation.
File summaries
File Summary Findings
projects/igniteui-angular/virtual-scroll/README.md Documents popup viewport initialization. Documents an unavailable input and behavior inconsistent with the implementation. (nit, 2 votes)
projects/igniteui-angular/grids/core/src/filtering/excel-style/excel-style-search.component.ts Migrates ESF scrolling and focus logic. Existing tests reference removed APIs. (critical, 2 votes)
Off-window active descendants can reference nonexistent elements. (moderate, 2 votes)
Estimated item size can select the wrong row. (moderate, 1 vote)
Combo migration is absent. (nit, 2 votes)
Navigation regression coverage is missing. (nit, 1 vote)
projects/igniteui-angular/grids/core/src/filtering/excel-style/excel-style-search.component.html Renders ESF options through virtual scroll. Virtualized options lack logical position and set size. (moderate, 2 votes)
The virtual-scroll list role disrupts listbox option ownership. (moderate, 2 votes)
Review details

Suppressed comments (2)

projects/igniteui-angular/grids/core/src/filtering/excel-style/excel-style-search.component.html:43

  • The Indigo ESF theme still applies its horizontal padding only to igx-display-container (themes/shared/_indigo.scss:122), but that element disappears with this replacement. The Indigo list rows will lose their established inset; retarget the style to the new virtual-scroll content/host as part of this migration.
    <igx-virtual-scroll
      #virtualScroll
      [data]="displayedListData"
      [estimatedItemSize]="itemSize"
      (stateChange)="onVirtualStateChange($event)"

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

  • The migration rewrites keyboard scrolling and active-descendant behavior, but the ESF grid specs have no coverage for listbox Arrow/Home/End navigation or off-window targets. Add Jasmine coverage that navigates beyond the rendered range and verifies scrolling, the active row, and a valid aria-activedescendant; this is where regressions in the new asynchronous path will surface.
    protected onVirtualStateChange(state: VirtualScrollState): void {
        this._renderedRange = state;
        this.refreshActiveDescendant();
  • Files reviewed: 3/3 changed files
  • Comments generated: 7
  • Review effort level: Balanced

Comment thread projects/igniteui-angular/virtual-scroll/README.md Outdated

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🟡 Changes recommended

Zero-sized viewports can incorrectly retain stale measurements; the stated consumer migrations and additional API coverage are also missing.

Once you've addressed the issues Copilot identified, you can request another Copilot review.

Review details

Suppressed comments (3)

projects/igniteui-angular/virtual-scroll/README.md:46

  • The claim that this input is never read again after a measurement is not true when orientation changes: the orientation effect resets _viewportSize to zero at virtual-scroll.component.ts:415-417, so a hidden host falls back to this input again. Either preserve a separate “already measured” state or document that the hint can become active again until the new axis is measured.
| `initialViewportSize` | `number` | `0` | Viewport size in pixels to render the **first** window against, for a list that cannot be measured when it is first rendered. A hint for that render only: once the host measures above zero, the measured size takes over and this input is not read again. Negative, `NaN` and infinite values count as no hint. See [Lists inside a popup](#lists-inside-a-popup). |

projects/igniteui-angular/virtual-scroll/src/virtual-scroll/virtual-scroll.component.ts:562

  • The hinted viewport is now used by scrollToIndex, but the added tests only cover rendered ranges and resizing; none calls scrollToIndex before the host has a real measurement. Please add a hidden/unmeasured-host test that verifies alignment (including nearest) uses initialViewportSize, since this is newly changed public behavior.
    if (
      requested === "nearest" &&
      this._engine.isIndexInView(index, current, this._effectiveViewportSize())

projects/igniteui-angular/virtual-scroll/src/virtual-scroll/virtual-scroll.component.ts:219

  • This new public input is user-visible API, but the current 22.2.0 changelog has no Virtual Scroll entry. Add it under New Features so consumers can discover the new popup-rendering contract.
  /**
   * Viewport size in pixels to render the first window against, for a list that is hidden
   * until the change detection pass that reveals it and so has no size to measure in it.
  • Files reviewed: 3/3 changed files
  • Comments generated: 2
  • Review effort level: Balanced

A list inside a popup is hidden until the change detection pass that
reveals it, so it has no size to measure and renders nothing in that
pass. initialViewportSize gives that first render a size to work from;
the measured size takes over as soon as the host can be measured.
@viktorkombov
viktorkombov force-pushed the vkombov/use-virtual-scroll-for-combo-esf branch from 733528c to 3220b2d Compare September 8, 2026 12:58

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🟡 Changes recommended

Paged mode has unbounded O(totalCount) memory usage, and the scope mismatch, ineffective assertions, and viewport-hint lifecycle issue remain unresolved.

Once you've addressed the issues Copilot identified, you can request another Copilot review.

Review details

Suppressed comments (2)

projects/igniteui-angular/virtual-scroll/src/virtual-scroll/virtual-scroll.component.spec.ts:1163

  • These bounds do not verify the documented normalization: an implementation that always used any finite value above 20 would pass every case. Add expected totals to the cases and assert the exact track height (20 items for NaN/infinite/negative and 400 items for 400.7).
                const height = Number.parseFloat(vsTrack(windowFixture).style.height);
                expect(Number.isFinite(height)).toBeTrue();
                expect(height).toBeGreaterThanOrEqual(20 * 50);

projects/igniteui-angular/virtual-scroll/src/virtual-scroll/virtual-scroll.component.ts:248

  • The stated ESF/combo refactor is not present in this change: both consumers still import and render IgxForOfDirective/*igxFor, while this line only adds a new virtual-scroll API. Please migrate those consumers and their tests, or retitle/rescope the pull request so its claimed purpose matches the implementation.
  public readonly dataWindow = input<VirtualDataWindow<T> | null>(null);
  • Files reviewed: 5/5 changed files
  • Comments generated: 4
  • Review effort level: Balanced

this._previousItems = loaded;
this._engine.resize(
items.length,
loaded.totalCount,
Comment thread CHANGELOG.md
Comment on lines +9 to +11
- `IgxVirtualScrollComponent`
- Added `initialViewportSize`, the viewport size to render the first window against. A list that is hidden until the change detection pass that reveals it has no size to measure in that pass and would render nothing; this gives that first render a size to work from, and the host's own size takes over once it has been laid out.
- Added `dataWindow`, taking a loaded page of a larger collection as `{ items, startIndex, totalCount }`. The list is as long as `totalCount`, so the scrollbar spans the whole collection while only the page is in memory, and indices the page does not cover render nothing until a page that covers them arrives. `data` is unchanged and is used whenever `dataWindow` is not set.
Comment on lines +485 to 487
// The size of the previous axis says nothing about the new one.
this._viewportSize.set(null);
this._measureViewport();
The search list is virtualized by IgxVirtualScrollComponent instead of
the igxFor directive. Rows are measured in the DOM rather than assumed
from a configured item and container size, and initialViewportSize gives
the list the height the menu allots it so the first window renders in
the pass that opens it.

aria-activedescendant now names only a rendered element: navigateItem
clears it while the target is off screen and restores it once the scroll
has rendered that row, and a scroll that recycles the wrappers no longer
leaves it pointing at an element that is gone.
Migrates IgxComboComponent and IgxSimpleComboComponent from the igxFor
directive to IgxVirtualScrollComponent, following the Excel style
filtering search list. IgxDropDownComponent now also accepts a
content-projected igx-virtual-scroll; *igxFor keeps working as
documented, and selection and navigation behave the same either way.

A row is measured in the DOM once it renders and the measured size
replaces the estimate it started from, so the scrollable extent follows
the real height of the rows that have rendered.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🟡 Changes recommended

Critical asynchronous paging/render-state defects and unresolved navigation and ARIA issues block approval.

Once you've addressed the issues Copilot identified, you can request another Copilot review.

Review details

Suppressed comments (3)

projects/igniteui-angular/combo/src/combo/combo.pipes.ts:14

  • Angular 22 treats pipes as standalone by default, and the project guidance explicitly avoids setting standalone: true on new declarations. Remove this redundant flag to keep the new pipe aligned with the current framework convention.
    standalone: true

projects/igniteui-angular/drop-down/README.md:87

  • The example nests the virtualizer's default role="list" inside the drop-down's listbox, but its children are role="option", not listitem. Applications following this example inherit an invalid ARIA structure. Show role="presentation" on the virtual-scroll host (and apply the same requirement to projected virtual-scroll guidance).
        <igx-virtual-scroll [data]="localItems" [estimatedItemSize]="28" [initialViewportSize]="200"
                            style="display: block; height: 200px">

projects/igniteui-angular/grids/core/src/filtering/excel-style/excel-style-search.component.html:39

  • igx-virtual-scroll supplies role="list" by default, but here it is nested inside a listbox and directly contains role="option" rows. That creates an invalid ARIA hierarchy because a list requires listitem descendants. Override the virtualizer to a presentational role so the options remain semantically owned by the outer listbox.
    <igx-virtual-scroll
  • Files reviewed: 27/27 changed files
  • Comments generated: 6
  • Review effort level: Balanced

Comment on lines +1105 to +1106
this._virtualizationState = { startIndex: state.startIndex, chunkSize };
this.dataPreLoad.emit({ ...this._virtualizationState, owner: this });
Comment on lines +118 to +120
public isIndexRendered(index: number): boolean {
return index >= this._rendered.startIndex && index <= this._rendered.endIndex;
}
Comment on lines +81 to +85
<igx-virtual-scroll #virtualScroll
[dataWindow]="itemWindow"
[estimatedItemSize]="estimatedItemSize"
[initialViewportSize]="viewportSize"
(stateChange)="handleVirtualStateChange($event)">
Comment on lines +83 to +85
public get length(): number {
const window = this._scroll.dataWindow();
return window ? window.totalCount : (this._scroll.data() ?? []).length;
Comment on lines +329 to 333
if (this.virtualization) {
newSelection = {
value: this.virtDir.igxForOf![index],
value: this.virtualization.itemAt(index),
index
} as IgxDropDownItemBaseDirective;
Comment on lines +77 to +81
<igx-virtual-scroll #virtualScroll
[dataWindow]="itemWindow"
[estimatedItemSize]="estimatedItemSize"
[initialViewportSize]="viewportSize"
(stateChange)="handleVirtualStateChange($event)">
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants