Skip to content

refactor(combo): overhaul selection, data pipeline, and component architecture#2164

Merged
rkaraivanov merged 7 commits intomasterfrom
rkaraivanov/refactor-combo
Apr 7, 2026
Merged

refactor(combo): overhaul selection, data pipeline, and component architecture#2164
rkaraivanov merged 7 commits intomasterfrom
rkaraivanov/refactor-combo

Conversation

@rkaraivanov
Copy link
Copy Markdown
Member

Description

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.tsDataControllerDataState

  • 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: findElementFromEventPathgetElementFromPath 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).

Type of Change

  • Refactoring (code improvements without functional changes)

Checklist

  • My code follows the project's coding standards
  • I have tested my changes locally
  • I have updated documentation if needed

…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).
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

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 Set with explicit one-way syncing between value and internal selection.
  • Replaced DataController with DataState using a dirty-flag + hostUpdate() to batch pipeline execution and support locale-aware collation.
  • Renamed findElementFromEventPathgetElementFromPath across 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.

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

Copilot reviewed 29 out of 29 changed files in this pull request and generated 1 comment.

@rkaraivanov rkaraivanov merged commit 7c7641c into master Apr 7, 2026
7 checks passed
@rkaraivanov rkaraivanov deleted the rkaraivanov/refactor-combo branch April 7, 2026 08:59
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants