refactor(combo): overhaul selection, data pipeline, and component architecture#2164
Merged
rkaraivanov merged 7 commits intomasterfrom Apr 7, 2026
Merged
refactor(combo): overhaul selection, data pipeline, and component architecture#2164rkaraivanov merged 7 commits intomasterfrom
rkaraivanov merged 7 commits intomasterfrom
Conversation
…hitecture
Major refactoring of the `IgcComboComponent` and its supporting infrastructure
to improve maintainability, correctness, and adherence to project conventions.
- `combo.ts`
- Inherits from `IgcBaseComboBoxComponent` instead of bare `LitElement`, sharing
open/close lifecycle (`_show`, `_hide`) with picker components.
- Removed the `SelectionController` — selection state (`_selected: Set<T>`) is now
managed directly inside the component for a simpler, single-source-of-truth model.
- Replaced `@watch('open')` decorator with `willUpdate()` lifecycle hook.
- Replaced `@queryAssignedElements` with `addSlotController` / `setSlots` for slot
detection (no more `inputPrefix` / `inputSuffix` public properties).
- Form value serialization moved into the `setFormValue` transformer on
`createFormValueState` — removes the separate `_setFormValue()` override.
- `_setDefaultValue` now handles invalid JSON gracefully instead of throwing.
- `firstUpdated` removed; initialization is handled by `willUpdate` / `willUpdate`
data-sync guard for the delayed-data scenario.
- Added `_syncSelectionFromValue()` (value → Set) and `_syncValueFromSelection()`
(Set → form value) for explicit one-way data flow.
- Added `_resolveItems()`, `_resolveItemValue()`, `_getValues()` private helpers.
- All internal render helpers and event handlers renamed to `_camelCase` convention
(e.g., `renderMainInput` → `_renderMainInput`, `handleMainInput` → `_handleMainInput`).
- `groupKey` / `groupSorting` setters removed — now simple `@property` declarations;
pipeline invalidation driven by `willUpdate`.
- `filteringOptions` and `disableFiltering` setters simplified.
- `label` and `placeholder` changed from `!` non-null asserted to optional (`?`).
- `placeholderSearch` setter typed as `string | undefined`.
- Added `Iterator.from` usage for lazy mapping/filtering of selections.
- `controllers/data.ts` — `DataController` → `DataState`
- Introduced a **dirty flag** pattern: `invalidate()` marks state as dirty and
`hostUpdate()` runs the pipeline lazily before each render, batching multiple
property changes into a single pipeline execution.
- Locale-aware `Intl.Collator` initialized from host locale; updated via
`updateLocale(locale)`.
- `dataState` is now a read-only accessor.
- Added `invalidate()` and `updateLocale()` as explicit public API.
- Internal pipeline methods prefixed with `_`.
- `controllers/navigation.ts`
- Removed all `// @ts-expect-error: protected access` hacks.
- Added `interactions` config object to decouple `hide`, `show`, `toggleSelection`,
`select`, and `clearSelection` callbacks — the navigation controller no longer
reaches into the combo's protected methods directly.
- `_active` field is now public (`active`) so it can be read without casting.
- `_getNearestItem` rewritten with a `for` loop (was `while` with off-by-one risk).
- Navigation mutations now call `requestUpdate('_activeIndex', previous)` with the
old value so Lit schedules targeted updates.
- Keybinding setup moved to the constructor body (was at the end).
- `controllers/selection.ts`
- **Deleted.** All selection logic inlined into `combo.ts`.
- `combo-item.ts`
- `renderCheckbox` renamed to `_renderCheckbox`.
- Switched from `nothing` conditional to Lit's `cache()` directive to preserve the
checkbox DOM subtree when `hideCheckbox` toggles.
- `connectedCallback` marked `@internal`.
- Missing `@attr` / `@default` JSDoc tags added.
- `combo-list.ts` / `combo-header.ts`
- Added explicit `void` return type to `register()` methods.
- `common/mixins/combo-box.ts`
- `IgcBaseComboBoxLikeComponent` renamed to `IgcBaseComboBoxComponent` (core open/
close behaviour only: `open`, `_show`, `_hide`, `_handleAnchorClick`, events).
- `IgcComboBoxBaseLikeComponent` extracted as a new subclass that adds
`keepOpenOnSelect` and `keepOpenOnOutsideClick` — used by Dropdown, Select,
DatePicker, and DateRangePicker.
- Protected event helpers renamed: `emitClosing` → `_emitClosing`, etc.
- `handleAnchorClick` → `_handleAnchorClick`.
- Utility functions (`getItems`, `getActiveItems`, etc.) given explicit return types.
- `setInitialSelectionState` simplified using `last()` and direct assignment.
- `common/util.ts`
- `findElementFromEventPath` renamed to `getElementFromPath` with full JSDoc.
- New `stopPropagation(event)` utility function extracted (used in combo template).
- `asArray` now uses `value == null` instead of `isDefined(value)`.
- Cross-component rename: `findElementFromEventPath` → `getElementFromPath`
Updated all callers: `button-group`, `calendar`, `carousel`, `drag`, `key-bindings`,
`root-click`, `date-picker`, `date-range-picker`, `dropdown`, `resize-controller`,
`select`, `stepper`, `tabs`, `tile`, `tree-item`.
---
- Added test: *"should handle invalid JSON in value attribute gracefully"*.
- Removed `await list.layoutComplete` from keyboard-selection test (no longer needed).
Contributor
There was a problem hiding this comment.
Pull request overview
Major refactor of IgcComboComponent and related infrastructure to simplify selection management, introduce lazy/batched data pipeline execution, and align internal APIs/naming with project conventions.
Changes:
- Refactored combo selection to a component-owned
Setwith explicit one-way syncing betweenvalueand internal selection. - Replaced
DataControllerwithDataStateusing a dirty-flag +hostUpdate()to batch pipeline execution and support locale-aware collation. - Renamed
findElementFromEventPath→getElementFromPathacross the codebase and updated combo-box base behavior (_show/_hide,_handleAnchorClick, event helpers).
Reviewed changes
Copilot reviewed 29 out of 29 changed files in this pull request and generated 2 comments.
Show a summary per file
| File | Description |
|---|---|
| stories/combo.stories.ts | Reorders Storybook controls/args for open. |
| src/components/tree/tree-item.ts | Updates event-path lookup helper rename. |
| src/components/tile-manager/tile.ts | Updates event-path lookup helper rename. |
| src/components/tabs/tabs.ts | Updates event-path lookup helper rename. |
| src/components/stepper/stepper.ts | Updates event-path lookup helper rename. |
| src/components/select/select.ts | Migrates to new combo-box base mixin and _handleAnchorClick naming. |
| src/components/resize-container/resize-controller.ts | Updates event-path lookup helper rename. |
| src/components/dropdown/dropdown.ts | Migrates to new combo-box base mixin and _handleAnchorClick naming; updates event-path helper. |
| src/components/date-range-picker/date-range-picker.ts | Migrates to new combo-box base mixin and _handleAnchorClick naming; updates event-path helper. |
| src/components/date-picker/date-picker.ts | Migrates to new combo-box base mixin and _handleAnchorClick naming; updates event-path helper. |
| src/components/common/util.ts | Introduces getElementFromPath + stopPropagation; tweaks asArray nullish handling. |
| src/components/common/mixins/combo-box.ts | Splits base combo-box behavior into IgcBaseComboBoxComponent + IgcComboBoxBaseLikeComponent; renames protected helpers; adds explicit return types. |
| src/components/common/controllers/root-click.ts | Updates event-path lookup helper rename. |
| src/components/common/controllers/key-bindings.ts | Updates event-path lookup helper rename. |
| src/components/common/controllers/drag.ts | Updates event-path lookup helper rename. |
| src/components/combo/operations/group.ts | Updates pipeline controller type (DataState). |
| src/components/combo/operations/filter.ts | Updates pipeline controller type (DataState) and adds explicit return type. |
| src/components/combo/controllers/selection.ts | Removes selection controller (selection logic moved into combo component). |
| src/components/combo/controllers/navigation.ts | Refactors navigation to use injected interaction callbacks; removes protected-access hacks; improves nearest-item logic and update scheduling. |
| src/components/combo/controllers/data.ts | Replaces eager pipeline runs with dirty-flag + lazy pipeline execution in hostUpdate(); adds locale update API. |
| src/components/combo/combo.ts | Overhauls combo architecture: inherits base combo-box component, inlines selection state, adds explicit sync helpers, updates slot handling and internal naming conventions. |
| src/components/combo/combo.spec.ts | Adjusts keyboard-selection test timing and adds invalid-JSON regression test. |
| src/components/combo/combo-list.ts | Adds explicit void return type for register(). |
| src/components/combo/combo-item.ts | Renames checkbox renderer, uses cache() to preserve checkbox subtree, and adds missing JSDoc tags/return types. |
| src/components/combo/combo-header.ts | Adds explicit void return type for register(). |
| src/components/carousel/carousel.ts | Updates event-path lookup helper rename. |
| src/components/calendar/helpers.ts | Updates event-path lookup helper rename. |
| src/components/calendar/calendar.ts | Updates event-path lookup helper rename. |
| src/components/button-group/button-group.ts | Updates event-path lookup helper rename. |
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
Major refactoring of the
IgcComboComponentand its supporting infrastructure to improve maintainability, correctness, and adherence to project conventions.combo.tsInherits from
IgcBaseComboBoxComponentinstead of bareLitElement, sharing open/close lifecycle (_show,_hide) with picker components.Removed the
SelectionController— selection state (_selected: Set<T>) is now managed directly inside the component for a simpler, single-source-of-truth model.Replaced
@watch('open')decorator withwillUpdate()lifecycle hook.Replaced
@queryAssignedElementswithaddSlotController/setSlotsfor slot detection (no moreinputPrefix/inputSuffixpublic properties).Form value serialization moved into the
setFormValuetransformer oncreateFormValueState— removes the separate_setFormValue()override._setDefaultValuenow handles invalid JSON gracefully instead of throwing.firstUpdatedremoved; initialization is handled bywillUpdate/willUpdatedata-sync guard for the delayed-data scenario.Added
_syncSelectionFromValue()(value → Set) and_syncValueFromSelection()(Set → form value) for explicit one-way data flow.Added
_resolveItems(),_resolveItemValue(),_getValues()private helpers.All internal render helpers and event handlers renamed to
_camelCaseconvention (e.g.,renderMainInput→_renderMainInput,handleMainInput→_handleMainInput).groupKey/groupSortingsetters removed — now simple@propertydeclarations; pipeline invalidation driven bywillUpdate.filteringOptionsanddisableFilteringsetters simplified.labelandplaceholderchanged from!non-null asserted to optional (?).placeholderSearchsetter typed asstring | undefined.Added
Iterator.fromusage for lazy mapping/filtering of selections.controllers/data.ts—DataController→DataStateIntroduced a dirty flag pattern:
invalidate()marks state as dirty andhostUpdate()runs the pipeline lazily before each render, batching multiple property changes into a single pipeline execution.Locale-aware
Intl.Collatorinitialized from host locale; updated viaupdateLocale(locale).dataStateis now a read-only accessor.Added
invalidate()andupdateLocale()as explicit public API.Internal pipeline methods prefixed with
_.controllers/navigation.tsRemoved all
// @ts-expect-error: protected accesshacks.Added
interactionsconfig object to decouplehide,show,toggleSelection,select, andclearSelectioncallbacks — the navigation controller no longer reaches into the combo's protected methods directly._activefield is now public (active) so it can be read without casting._getNearestItemrewritten with aforloop (waswhilewith off-by-one risk).Navigation mutations now call
requestUpdate('_activeIndex', previous)with the old value so Lit schedules targeted updates.Keybinding setup moved to the constructor body (was at the end).
controllers/selection.tsDeleted. All selection logic inlined into
combo.ts.combo-item.tsrenderCheckboxrenamed to_renderCheckbox.Switched from
nothingconditional to Lit'scache()directive to preserve the checkbox DOM subtree whenhideCheckboxtoggles.connectedCallbackmarked@internal.Missing
@attr/@defaultJSDoc tags added.combo-list.ts/combo-header.tsAdded explicit
voidreturn type toregister()methods.common/mixins/combo-box.tsIgcBaseComboBoxLikeComponentrenamed toIgcBaseComboBoxComponent(core open/ close behaviour only:open,_show,_hide,_handleAnchorClick, events).IgcComboBoxBaseLikeComponentextracted as a new subclass that addskeepOpenOnSelectandkeepOpenOnOutsideClick— used by Dropdown, Select, DatePicker, and DateRangePicker.Protected event helpers renamed:
emitClosing→_emitClosing, etc.handleAnchorClick→_handleAnchorClick.Utility functions (
getItems,getActiveItems, etc.) given explicit return types.setInitialSelectionStatesimplified usinglast()and direct assignment.common/util.tsfindElementFromEventPathrenamed togetElementFromPathwith full JSDoc.New
stopPropagation(event)utility function extracted (used in combo template).asArraynow usesvalue == nullinstead ofisDefined(value).Cross-component rename:
findElementFromEventPath→getElementFromPathUpdated all callers:button-group,calendar,carousel,drag,key-bindings,root-click,date-picker,date-range-picker,dropdown,resize-controller,select,stepper,tabs,tile,tree-item.Added test: "should handle invalid JSON in value attribute gracefully".
Removed
await list.layoutCompletefrom keyboard-selection test (no longer needed).Type of Change
Checklist