Skip to content

feat(combo): add remote data, select all, group selection and confirm mode - #2545

Merged
ChronosSF merged 42 commits into
masterfrom
rkaraivanov/combo-enhancements
Nov 12, 2026
Merged

ChronosSF merged 42 commits into
masterfrom
rkaraivanov/combo-enhancements

Conversation

@rkaraivanov

Copy link
Copy Markdown
Member

Description

Remote data (#2519):

  • filteringOptions.mode 'remote' with igcSearchInput and igcDataRequest
    lets the application load the options page by page.
  • totalItemCount adds skeleton placeholders for the items to load, and
    loading shows an igc-circular-progress in place of the toggle icon.
  • A new data keeps the selection, also the items that it does not hold.
  • bindComboDataSource() in igniteui-webcomponents/extras implements the
    flow on the public API.

Selection (#2533, #2535):

  • selectAllItems(), deselectAllItems() and the showSelectAll option act
    on the items that the list shows.
  • groupSelection makes each group header an option for its items.
  • selectionCommit="confirm" holds the changes of the user in a pending
    selection (pendingValue, igcPendingChange). Enter or the built-in
    Apply commits it with one igcChange of the commit type. Cancel,
    commitSelection() and cancelSelection() complete the API.
  • igcClosing and igcClosed report the trigger that closes the list.

Fixes (#2529, #2530):

  • Tab moves through the focusable content of the header, empty and
    footer slots.
  • The inner igcChange, igcStateChange and igcDataRequest events stay
    inside the combo.
  • The focus moves to the main input when the list closes around it.
  • igcChange.newValue has the order of the value that follows.

Internals:

  • combo.ts splits into ComboSelection, ComboDataRequests and DataState
    helpers with unit specs, and combo.spec.ts splits into four files.
  • igniteui-i18n-core and igniteui-i18n-resources 2.0.0-rc.3 bring the
    select-all, loading, Cancel and Apply strings.
  • New stories and an STE specification cover the new features.

Type of Change

  • Bug fix (non-breaking change that fixes an issue)
  • New feature (non-breaking change that adds functionality)
  • Documentation update

Related Issues

Closes #2519
Closes #2529
Closes #2530
Closes #2533
Closes #2535

Checklist

  • My code follows the project's coding standards
  • I have tested my changes locally
  • I have updated documentation if needed
  • If I changed the public API on purpose, I have run npm run public-api:update and committed public-api.json
  • I have added a CHANGELOG.md entry under [Unreleased]

…side

The igcChange of the search input no longer reaches listeners on the combo.
The igcStateChange and igcDataRequest of the inner list stop at the list.

Closes #2530.
While the list is open, Tab and Shift+Tab move through the filtering input,
the header content, the list, the empty content and the footer content,
and close the list past either end.
getFocusableElements in internals/utils/dom walks the flat tree,
and the focus trap uses it.

Closes #2529. Refs #2533.
igcClosing and igcClosed carry { trigger }.
ToggleController.hide takes a detail for one call.

Refs #2533.
filteringOptions.mode 'remote', igcSearchInput, igcDataRequest in remote mode,
and a selection that a new data keeps. The igcInput of the inner inputs stays inside.

Refs #2519.
Binds a combo to a server that searches and pages, on the public combo API.

Refs #2519.
… 2.0.0-rc.2

The combo needs the combo_loading and combo_select_all strings.
In remote mode, totalItemCount adds a placeholder option for each item to load,
and igcDataRequest asks for the rendered placeholders, up to the total.
loading shows a progress bar that the loading slot can replace.

Refs #2519.
The binding keeps the total of each search, ends the results on an empty page,
and loads max(pageSize, count) items per request.

Refs #2519.
showSelectAll puts a Select all option first in multiple selection.
It selects or deselects the shown items with one igcChange,
and its checkbox exports the indeterminate part.

Refs #2535.
…s skeleton rows

While loading, a spinner covers the toggle icon, which keeps its box,
so the input does not change its width, and an empty list shows Loading
instead of the empty message. The loading part is the spinner and the
loading slot replaces it.
Each placeholder is the skeleton of an item: the box of the checkbox and
a bar with a wave, in the text color of the placeholder part.
When the list closes while the focus is in it, for example on hide() f
rom a footer button, the focus moves to the main input.
…ion features of the combo

RemoteData shows a server log and a combo with styled loading parts.
LazyLoading sets loading and replaces the spinner through the loading slot.
ColumnChooser uses the select-all option. T
he new StagedFilter story applies labels on Apply or Enter and puts them back on the other close triggers.
groupSelection makes each group header an option in multiple selection with groupKey.
A click or Space on it selects the shown items of its group,
or deselects them when all of them are selected, with one igcChange,
and its checkbox is indeterminate while only some are selected.
The header keeps the text style of a group header.
The select-all option and the header options now have aria-posinset and aria-setsize,
because Chromium moves the positions after an option without them.
With selection-commit="confirm", the open list keeps a pending selection.
Each toggle emits the cancelable igcPendingChange, Enter applies the pending selection
with one igcChange of the new commit type, and any other close drops it.

The selection methods act on the pending selection while it exists;
pendingValue, commitSelection() and cancelSelection()
serve Apply and Cancel buttons in the footer.
The loading spinner in place of the toggle icon is now an indeterminate
igc-circular-progress instead of a CSS spinner, in the text color and
the size of the icon, centered on the icon.
It renders only while loading is set.
The paging logic of igcDataRequest moves from combo.ts to a plain class
with its own unit specs. The class reads a narrow view of the host and
of DataState, and the host sends the event through a request callback.

No public API or behavior change.
The items and the selection state of a toggle option (the select-all
option and a group header) now come from DataState: itemsOf(index),
selectionOf(index, selected) and shownItems(). DataState reads a narrow
view of the host, so its new unit specs use a plain object. The host
keeps the update flush before it reads the shown items.

No public API or behavior change.
The committed and pending selections, the value index and the selection
math move from combo.ts to a plain class with its own unit specs. The
class sends no events and exposes the sets as read-only views, so only
its methods change them. combo.ts keeps the flow: the change events,
the form value, the display text and the search text. isEmpty now takes
read-only sets and maps.

No public API or behavior change.
combo.ts uses one region style and groups its members by concern: open
and close, the selection flow, the event handlers and the rendering.
ComboHost is gone, so types.ts no longer imports combo.ts, and the
navigation controller takes the combo type. A few internal comments are
shorter or more exact.

No public API or behavior change.
The 4,474-line combo spec moves into combo.spec.ts (the component,
slots, close trigger, scroll strategy, theming and ARIA),
combo-remote.spec.ts, combo-selection.spec.ts and combo-form.spec.ts.
The shared data and helpers move to src/internals/testing/combo.spec.ts.
Each file keeps the root describe('Combo'), so all test titles stay the
same, and the moved blocks do not change. The form file gets an axe
spec for a failed submit. spec.md lists the new files.
Merge the revision entries of this branch into five, one for each
feature, and reword the specification in Simplified Technical English.
Select all gets its own section, the confirm mode and the remote data
sections use short labelled bullets, and the test scenarios follow the
four spec files. Correct three statements: aria-activedescendant is on
the listbox, a character key filters only in single selection, and
Enter needs an active option outside the confirm mode.
@rkaraivanov
rkaraivanov requested a review from ChronosSF October 28, 2026 10:12
@YoannaIvanova

YoannaIvanova commented Nov 5, 2026 •

Copy link
Copy Markdown

With groupSelection enabled, the group header moves right and aligns with the items, which removes the visual hierarchy. Could the items move further right, so they are indented under the header as in the Tree component?

Screenshot 2026-11-05 at 2 01 30 PM Screenshot 2026-11-05 at 2 02 53 PM

- A click on the icon with a search text emits igcSearchInput again, so a
  remote app reads filteringOptions.caseSensitive and searches again.
  A filtering options change in remote mode does not rebuild the records.
- bindComboDataSource() gives caseSensitive to load(), and a case change
  from the icon loads the first page again.
- Spec: say that the select-all igcChange is cancelable, that groups have
  no entry in the selection, and that the app removes deleted remote
  selections.
- With groupSelection, the items are one level in under their header,
  as in the tree. The select-all option stays at the header level.
- The new --ig-combo-item-indent CSS property changes the indent.
- Stories: sync the caseSensitiveIcon description with its JSDoc.
- With reduced motion, the indeterminate animation runs at a third of
  its speed, and a new value shows at once.
- In forced colors, the ring takes the text color and the track is
  GrayText, also with a custom gradient.
- The host is an inline block with no line height, so its box is the
  ring. The combo and the LazyLoading story drop their display: flex.
- Tree, chip and stepper size the ring with --ig-circular-bar-diameter.
- With reduced motion, the indeterminate bars run at a third of their
  speed, and a new value shows at once. A duration set on the host or
  on the bar parts is slowed too.
- A shared reduced-motion() mixin in the progress animations keeps the
  circular and the linear indicators in step.
- In multiple selection, the options report the state of their checkbox
  in aria-checked, not aria-selected, as the APG listbox pattern allows.
  The select-all and group header options report mixed while only some
  of their items are selected.
- Single selection keeps aria-selected.
- Shared ariaOf spec helper; spec.md revision 21; CHANGELOG.
rkaraivanov and others added 3 commits November 6, 2026 19:16
- Combo: _onTab uses focusTabStop, and the base class returns the focus
  on close (_popupElement). A slotted control that handles Tab or Escape
  keeps the key.
- Date picker, date range picker, color picker: a closed picker no longer
  cancels Escape, so an open combo, dropdown or select around it closes.
- simulateCancelableKeydown testing helper.
Comment thread src/extras/index.ts Resolved
Comment thread src/components/combo/combo.ts Resolved
Comment thread src/extras/combo-data-source.ts Resolved
Comment thread src/components/combo/combo.ts Resolved
Comment thread src/extras/combo-data-source.ts Resolved
- Extras: bindComboDataSource moves to
  igniteui-webcomponents/extras/combo-data-source, so it no longer pulls
  in the chat Markdown peers.
- Combo, remote mode: new results (the first data after a search, a
  shorter data, another first valueKey) clear the active option and show
  from the top, also in a list that opens later. An appended page keeps
  the place.
- Combo, selection: a value without a record stays in value until a
  change names it (deselect, the clear icon, a new value). A resolve()
  answer selects only values that are still in value.
- Combo: filteringOptions applies at once. A write of the same options
  does not update the combo.
- Key bindings: keys that an IME handles are skipped for every component
  (combo, date time input, date picker, color picker, mask input).
- Virtual scroll: a programmatic scroll and a resize sync the cached
  offset for the next render.
…nhancements

Merge #2590 (Enter submits the form as in a native form) and apply it
to the combo.

- Resolve conflicts in the revision tables of the date picker, date
  range picker, date time input and mask input spec.md files. Keep the
  rows from both sides and renumber the master rows last.
- With the list closed, Enter in single selection submits the form as
  in a native input with a list, and the combo counts as a text field.
  With the list open, Enter belongs to the list.
- Multiple selection does not submit and is not a text field, as a
  native select.
- The main input Enter binding runs only while the list is open, so a
  closed list no longer cancels the keydown and its keypress.
- Update the combo and input spec.md files, the CHANGELOG and the
  combo form specs.
@ChronosSF
ChronosSF self-requested a review November 12, 2026 07:12
@ChronosSF
ChronosSF merged commit cdae8ff into master Nov 12, 2026
10 checks passed
@ChronosSF
ChronosSF deleted the rkaraivanov/combo-enhancements branch November 12, 2026 11:59
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment