Skip to content

feat: DH-19683: Multi-select combobox component#2671

Closed
mofojed wants to merge 15 commits into
deephaven:mainfrom
mofojed:DH-19683-multi-select-component-in-deephaven-ui
Closed

feat: DH-19683: Multi-select combobox component#2671
mofojed wants to merge 15 commits into
deephaven:mainfrom
mofojed:DH-19683-multi-select-component-in-deephaven-ui

Conversation

@mofojed
Copy link
Copy Markdown
Member

@mofojed mofojed commented May 6, 2026

Add multi-select combobox component. Tested with (and required for) deephaven/deephaven-plugins#1347

@codecov
Copy link
Copy Markdown

codecov Bot commented May 6, 2026

Codecov Report

❌ Patch coverage is 57.58065% with 263 lines in your changes missing coverage. Please review.
✅ Project coverage is 50.50%. Comparing base (d76c08b) to head (55d6dea).
⚠️ Report is 11 commits behind head on main.

Files with missing lines Patch % Lines
...omponents/src/spectrum/multiSelect/MultiSelect.tsx 1.35% 146 Missing ⚠️
...src/spectrum/multiSelect/useMultiSelectKeyboard.ts 45.00% 66 Missing ⚠️
...mponents/src/spectrum/utils/useMultiPickerProps.ts 80.55% 21 Missing ⚠️
...trum/multiSelect/useMultiSelectNormalizedProps.tsx 0.00% 17 Missing ⚠️
...ts/src/spectrum/multiSelect/MultiSelectListBox.tsx 0.00% 4 Missing ⚠️
...src/spectrum/multiSelect/MultiSelectNormalized.tsx 0.00% 3 Missing ⚠️
...ents/src/spectrum/multiSelect/multiSelectUtils.tsx 97.36% 2 Missing ⚠️
...ectrum/multiSelect/useMultiSelectLoadingSpinner.ts 93.10% 2 Missing ⚠️
...ectrum/multiSelect/useMultiSelectScrollListener.ts 95.83% 1 Missing ⚠️
...src/spectrum/utils/useStringifiedMultiSelection.ts 85.71% 1 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main    #2671      +/-   ##
==========================================
+ Coverage   49.76%   50.50%   +0.74%     
==========================================
  Files         774      787      +13     
  Lines       43945    44782     +837     
  Branches    11330    11587     +257     
==========================================
+ Hits        21871    22619     +748     
- Misses      22027    22117      +90     
+ Partials       47       46       -1     
Flag Coverage Δ
unit 50.50% <57.58%> (+0.74%) ⬆️

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@jnumainville jnumainville assigned jnumainville and unassigned mofojed May 14, 2026
Copy link
Copy Markdown
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

Adds a new table-backed multi-select combobox for Deephaven’s Spectrum-based UI stack, introducing a reusable @deephaven/components MultiSelect (and normalized variant) plus a @deephaven/jsapi-components wrapper that binds it to DH tables/viewporting and server-side search.

Changes:

  • Introduces MultiSelect / MultiSelectNormalized components (tags + inline search + multi-selection ListBox) with supporting hooks/utilities, styles, and tests.
  • Adds useMultiPickerProps + jsapi-components MultiSelect wrapper to drive the component from a DH table with viewporting + search + label caching.
  • Updates multi-selection stringification to preserve selected keys that are filtered out of the current items list.

Reviewed changes

Copilot reviewed 30 out of 30 changed files in this pull request and generated 2 comments.

Show a summary per file
File Description
packages/jsapi-components/src/spectrum/utils/useMultiPickerProps.ts New hook deriving MultiSelect props from a DH table, including snapshot-based label resolution and label caching.
packages/jsapi-components/src/spectrum/utils/index.ts Re-exports the new useMultiPickerProps utility.
packages/jsapi-components/src/spectrum/MultiSelect.tsx JSAPI-facing MultiSelect wrapper around MultiSelectNormalized, including open/input search behavior.
packages/jsapi-components/src/spectrum/MultiSelect.test.tsx Unit tests validating wrapper behavior for open/close and search text forwarding.
packages/jsapi-components/src/spectrum/MultiPickerProps.ts Defines table-backed prop shape for the JSAPI MultiSelect wrapper.
packages/jsapi-components/src/spectrum/index.ts Exports the new JSAPI MultiSelect and MultiPickerProps.
packages/components/src/spectrum/utils/useStringifiedMultiSelection.ts Preserves selected keys not present in current normalized items (server-side filtering support).
packages/components/src/spectrum/multiSelect/useMultiSelectState.ts New hook managing controlled/uncontrolled multi-selection state and preserving selection across filtering.
packages/components/src/spectrum/multiSelect/useMultiSelectState.test.ts Tests for useMultiSelectState behavior (controlled/uncontrolled, toggle, filtered preservation).
packages/components/src/spectrum/multiSelect/useMultiSelectScrollListener.ts New hook attaching/removing scroll listeners to the popover scroll area.
packages/components/src/spectrum/multiSelect/useMultiSelectScrollListener.test.ts Tests for scroll listener attachment, invocation, and cleanup.
packages/components/src/spectrum/multiSelect/useMultiSelectNormalizedProps.tsx Converts normalized items/sections to JSX children and stringifies multi-selection for Spectrum interop.
packages/components/src/spectrum/multiSelect/useMultiSelectLoadingSpinner.ts Debounced inline loading spinner visibility logic based on loading state + open state + trigger mode.
packages/components/src/spectrum/multiSelect/useMultiSelectLoadingSpinner.test.ts Tests for spinner debounce and visibility behavior across states.
packages/components/src/spectrum/multiSelect/useMultiSelectKeyboard.ts Keyboard handling + virtual focus management for MultiSelect (arrows/enter/escape/backspace, aria-activedescendant).
packages/components/src/spectrum/multiSelect/useMultiSelectKeyboard.test.ts Tests validating keyboard handler behavior.
packages/components/src/spectrum/multiSelect/useMultiSelectFilter.ts New hook managing controlled/uncontrolled search text and client-side filtering (or skipping for server-side).
packages/components/src/spectrum/multiSelect/useMultiSelectFilter.test.ts Tests for filtering behavior and controlled input handling.
packages/components/src/spectrum/multiSelect/multiSelectUtils.tsx Utilities to flatten JSX children, filter entries, and resolve selection to string sets.
packages/components/src/spectrum/multiSelect/multiSelectUtils.test.tsx Tests for the MultiSelect utility functions.
packages/components/src/spectrum/multiSelect/MultiSelectTag.tsx Tag UI subcomponent with remove button for selected items.
packages/components/src/spectrum/multiSelect/MultiSelectTag.test.tsx Tests for tag rendering and remove behavior.
packages/components/src/spectrum/multiSelect/MultiSelectProps.ts Public props for MultiSelect plus MultiSelectNormalizedProps for normalized-item usage.
packages/components/src/spectrum/multiSelect/MultiSelectNormalized.tsx Normalized variant that adapts normalized items/sections into the base MultiSelect.
packages/components/src/spectrum/multiSelect/MultiSelectListBox.tsx Private popover content component rendering either empty state or a Spectrum ListBox.
packages/components/src/spectrum/multiSelect/MultiSelect.tsx Core MultiSelect implementation (Field wrapper, tags + input trigger, popover, selection/filter/keyboard integration).
packages/components/src/spectrum/multiSelect/MultiSelect.scss Styling to match Spectrum ComboBox visuals (trigger, tags, input, focus/invalid/quiet variants).
packages/components/src/spectrum/multiSelect/index.ts Exports MultiSelect modules from the new multiSelect package folder.
packages/components/src/spectrum/index.ts Re-exports multiSelect from the main Spectrum entrypoint.
packages/components/package.json Adds React Spectrum/Aria/Stately + icon dependencies required by MultiSelect.
Comments suppressed due to low confidence (1)

packages/jsapi-components/src/spectrum/utils/useMultiPickerProps.ts:261

  • ItemSelection in this codebase is 'all' | Set<ItemKey>, but this effect only handles Array and 'all'. When selectedKeys/defaultSelectedKeys is a Set (the common case), selectedKeySet becomes an empty Set, so no labels are cached and selectedItemLabels never populates. Update this logic to handle Set/iterables (e.g., currentSelectedKeys instanceof Set or currentSelectedKeys != null && currentSelectedKeys !== 'all') and build selectedKeySet from it.
      let selectedKeySet: Set<string> | null;
      if (currentSelectedKeys instanceof Array) {
        selectedKeySet = new Set(currentSelectedKeys.map(String));
      } else if (currentSelectedKeys === 'all') {
        selectedKeySet = null;
      } else {
        selectedKeySet = new Set<string>();
      }

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +144 to +146
let isCanceled = false;
hasLoadedSnapshotRef.current = true;

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.

Reworked

Comment thread packages/components/src/spectrum/multiSelect/useMultiSelectLoadingSpinner.test.ts Outdated
@jnumainville jnumainville marked this pull request as ready for review May 18, 2026 20:27
@jnumainville
Copy link
Copy Markdown
Contributor

Will be continued in #2685

@github-actions github-actions Bot locked and limited conversation to collaborators May 19, 2026
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants