fix: validate active item after GridView filtering#515
Conversation
When filtering hides the current active item (the one with tabIndex=0), no visible item remains tabbable, breaking Tab key navigation. Add _validateActiveItem() to reassign the roving tabIndex to the first visible item after each filter batch, matching the Table's existing _validateActiveRow() pattern. Made-with: Cursor
There was a problem hiding this comment.
Pull request overview
Fixes GridView roving tabIndex behavior when filtering hides the currently active item, ensuring keyboard Tab navigation always has a visible tabbable item.
Changes:
- Add
_validateActiveItem()to reassign the active (tabbable) item when the current active item is hidden. - Invoke
_validateActiveItem()after synchronousfilter()runs. - Invoke
_validateActiveItem()after each batch infilterAsync().
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
- Use _setActiveItem(null) instead of manual tabIndex/null mutation - Add destroyed check to _validateActiveItem early return - Reuse _validateActiveItem in _onRemoveGridViewItem - Add unit tests for filter() and filterAsync() active item reassignment Made-with: Cursor
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 2 out of 2 changed files in this pull request and generated 1 comment.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
Track whether the active item was hidden during the current batch and only call _validateActiveItem() when needed, avoiding a full DOM scan on every frame for large grids. Made-with: Cursor
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 2 out of 2 changed files in this pull request and generated 1 comment.
Comments suppressed due to low confidence (1)
src/components/GridView/index.ts:460
- In
filterAsync(), the cancel path (_filterCanceled) returns before_validateActiveItem()can run. Sincechild.hidden = trueemits a synchronoushideevent, external listeners could callfilterAsyncCancel()mid-batch after the active item is hidden; that would trigger this early-return and leavetabIndex=0on a now-hidden active item (the same a11y issue this PR is fixing). Consider validating the active item before emittingfilter:cancel/returning (either unconditionally on cancel, or when the active item became invalid during the batch).
if (this._filterCanceled) {
this._filterCanceled = false;
this.emit('filter:cancel');
return;
}
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
Summary
GridViewwhen filtering hides the active item (the one holdingtabIndex=0)_validateActiveItem()that reassigns the roving tabIndex to the first visible item when the current active item is hidden or destroyedfilter(), and conditionally infilterAsync()only when the active item is hidden during the current batch_validateActiveItem()in_onRemoveGridViewItemto eliminate duplicate scan logicBackground
GridViewuses a roving tabIndex pattern where only one item hastabIndex=0and all others havetabIndex=-1. WhenfilterAsync()hides items (e.g. when navigating folders in the Editor's Assets panel), the active item can become hidden. Sincedisplay: noneremoves it from the tab order, no visible item is tabbable and Tab does nothing.Test Plan
examples/accessibility/tabindex.htmland verify roving tabIndex still works correctly (Tab into GridView selects the active item, arrow keys navigate, status bar shows 1 tab stop per widget)