Replace SelectField with a virtualized Combobox for large-schema selects - #2113
Open
mjuarros wants to merge 8 commits into
Open
Replace SelectField with a virtualized Combobox for large-schema selects#2113mjuarros wants to merge 8 commits into
mjuarros wants to merge 8 commits into
Conversation
Backs large option lists (5,000-10,000+ items) with @tanstack/react-virtual so rendering stays smooth instead of freezing the UI, matching the look and keyboard/animation behavior of the existing Select component. Also supports an inner label above the value, matching SelectField's labelPlacement="inner" style, so sites migrating off SelectField don't lose that caption.
Swaps the node-type dropdowns in FilterSearchTabContent and DataExplorer over to the virtualized Combobox to fix UI freezing on schemas with large numbers of vertex/edge types. DataExplorer uses the inner label to keep the same caption the SelectField's labelPlacement="inner" showed.
The previous commit shipped a hand-rolled Combobox instead of the @base-ui/react primitives the proposal called for. This finishes that migration, then fixes what verifying it surfaced: a popup width mismatch, an arrow button that didn't close on a second click, an entrance animation twice as long as every other dropdown, and an empty-state element reserving phantom space above the list. The significant fix: the virtualizer was measuring the wrong scroll element, so it rendered nearly all 10,000 options as real DOM nodes on every open instead of ~20 - confirmed with an isolated Chromium harness (~1.75s per open before, ~30ms after). Also fixes a memoization bug in useSearchableAttributes surfaced while restoring its referential-stability guarantee, and updates the test suite's offsetHeight/offsetWidth mock to actually distinguish which element gets measured.
…bility issues The virtualized prop was missing from BaseCombobox.Root, silently bounding keyboard navigation to whatever was mounted in the DOM window instead of the full option count. Open/filter state was smeared across handlers that didn't distinguish typed input from Base UI's own programmatic syncs, causing stale display text and a popup that never closed on focus-out. The toggle button is no longer hidden from the accessibility tree: when the input already has a value, VoiceOver's Read-All treats it as content to read and skips announcing its combobox role, so the button is what re-announces it in that case. Also fixes popup sizing/animation, prop passthrough safety, filtering efficiency, and several tests that didn't assert what they claimed.
useTranslations() returned a fresh function on every render, which fed into useAttributeOptions's own useMemo dependency array and defeated it every render regardless of whether the underlying data changed. vertexOptions had the same problem one level up: a fresh array of fresh objects on every render, defeating Combobox's own filteredOptions memo on every keystroke.
…ive nanoid @base-ui/react and @tanstack/react-virtual were declared in both the workspace root and packages/graph-explorer, though only graph-explorer imports them. Also adds an override for nanoid (GHSA-2v37-7h3g-55p8), pulled in transitively via vite > postcss and confined to dev, following the existing override pattern for esbuild/dompurify/undici.
…-filter support Adds an ADR for introducing @base-ui/react and @tanstack/react-virtual alongside the existing Radix/shadcn stack, exceptions to the React Compiler guidance for the memoization and useVirtualizer cases this PR needed, the Base UI vs Radix data-attribute divergence, and the jsdom offsetHeight gotcha the test suite works around. Also updates user-facing docs to mention that the node-type/attribute pickers now support typing to filter.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Description
SelectFieldrenders every option as a DOM node unconditionally. With a schema of 10,000+ vertex/edge types, this froze the UI for several seconds every time the Data Explorer node-type picker, the Search Sidebar's node-type filter, or its attribute filter was opened.This replaces those three pickers with a new
Comboboxcomponent built on Base UI's accessible combobox primitives plus@tanstack/react-virtualfor windowed rendering, so only the visible options (~20) are ever mounted regardless of how many types the schema has. Type-to-filter narrows the list as you type. Every otherSelectFieldusage in the app is untouched — they're all either fixed-size enums or scoped to a single vertex/edge type's own attributes, so they were never at risk of this.How to read
Combobox.tsx— the new component; start here.DataExplorer.tsx,FilterSearchTabContent.tsx,useKeywordSearch.ts— the three sites swapped fromSelectFieldtoCombobox.displayTypeConfigs.ts— a small pre-existing memoization bug inuseSearchableAttributesfixed along the way (found while restoring a referential-stability guarantee post-migration); tangential to the main change.Combobox.test.tsx,useKeywordSearch.test.ts— tests, including a 10,000-item scale guardrail.Validation
Manually validated against a synthetic 10,000-vertex-type / 10,000-edge-type dataset: the Data Explorer and Search Sidebar pickers previously froze the page for several seconds on open; they now open instantly regardless of schema size. Confirmed with an isolated real-Chromium test harness that open latency stays flat (~30ms) across 10, 500, and 10,000 items — a virtualizer-wiring bug that briefly regressed this was caught the same way and fixed before landing.
pnpm checksandpnpm testpass (219 test files, 2696 tests).Related Issues
Check List
pnpm checkspasses with no errors.pnpm testpasses with no failures.