Skip to content

[WC-3347]: fix(combobox-web): keep filter text cleared after select-all + Backspace - #2386

Open
samuelreichert wants to merge 4 commits into
mainfrom
WC-3347-combobox-multiselect-backspace
Open

[WC-3347]: fix(combobox-web): keep filter text cleared after select-all + Backspace#2386
samuelreichert wants to merge 4 commits into
mainfrom
WC-3347-combobox-multiselect-backspace

Conversation

@samuelreichert

@samuelreichert samuelreichert commented Aug 14, 2026

Copy link
Copy Markdown
Contributor

Pull request type

Bug fix (non-breaking change which fixes an issue)


Description

Reported by a customer: in a multi-select Combobox, type filter text, press Ctrl/Cmd+A, press Backspace. The input looks empty — but click outside the Combobox and back in, and the text is there again. Using Delete instead of Backspace works correctly.

Root cause. MultiSelection.tsx layers a custom onKeyDown on top of downshift's input props, gated on inputRef.current?.selectionStart === 0:

if (
    (event.key === "Backspace" && inputRef.current?.selectionStart === 0) ||
    (event.key === "ArrowLeft" && isSelectedItemsBoxStyle && inputRef.current?.selectionStart === 0)
) {
    setActiveIndex(selectedItems.length - 1);
}

Fix. downshift already ships exactly the predicate this handler needs, and applies it to its own dropdown Backspace handling — but does not export it. Mirror it locally as isChipNavigationPermitted: no modifier held, and either an empty input or a caret collapsed at position 0. The ArrowLeft branch carried the identical faulty check and is corrected with it. The helper carries a comment naming the downshift version and source file so a future upgrade re-checks it.

What should be covered while testing?

Multi-select Combobox with at least one already-selected chip (with zero chips setActiveIndex(-1) is a no-op and the bug never surfaces):

  1. Type filter text → Ctrl/Cmd+A → Backspace. Input clears, focus stays in the input, no chip becomes active. Click outside, click back in — still empty.
  2. Same with Delete — unchanged behaviour.
  3. Select only part of the text starting at position 0, press Backspace — only the selected characters go, trailing text survives.
  4. Empty filter input + Backspace — last chip becomes active and can be removed (regression risk: this must still work).
  5. selectedItemsStyle="boxes": ArrowLeft with a collapsed caret at position 0 reaches the chips; ArrowLeft with text selected stays in the input.
  6. Single-select Combobox: Backspace on an empty input still clears the selection.

@samuelreichert
samuelreichert requested a review from a team as a code owner August 14, 2026 14:43
@github-actions

This comment has been minimized.

@samuelreichert
samuelreichert force-pushed the WC-3347-combobox-multiselect-backspace branch from 775ca15 to 9e0e5af Compare August 17, 2026 08:51
@samuelreichert
samuelreichert force-pushed the WC-3347-combobox-multiselect-backspace branch from 9e0e5af to f1e2229 Compare August 17, 2026 08:51
@github-actions

Copy link
Copy Markdown
Contributor

AI Code Review

⚠️ Approved with suggestions — low-severity items only, safe to merge


What was reviewed

File Change
packages/pluggableWidgets/combobox-web/src/components/MultiSelection/MultiSelection.tsx Extracted isChipNavigationPermitted helper replacing faulty selectionStart === 0 gate
packages/pluggableWidgets/combobox-web/src/__tests__/MultiSelection.spec.tsx 8 new unit tests for filter-input key routing (WC-3347 describe block)
packages/pluggableWidgets/combobox-web/e2e/ComboboxMultiSelectionKeys.spec.js New E2E regression spec for Backspace/Delete with select-all text
packages/pluggableWidgets/combobox-web/CHANGELOG.md User-facing entry under [Unreleased]
packages/pluggableWidgets/combobox-web/openspec/... Archived OpenSpec change — not reviewed (design/spec artefacts)

Skipped (out of scope): dist/, pnpm-lock.yaml, OpenSpec archive files


Findings

⚠️ Low — Snyk security CI check failing

Check: security/snyk (AppDev AppStore content)FAILURE
Problem: The Snyk security check is red on this PR. It may be a pre-existing failure unrelated to this change, but it should be investigated before merge to confirm it is not a newly introduced dependency vulnerability.
Fix: Check the Snyk report at the linked URL in the PR checks. If it is pre-existing, add a note to the PR description confirming that. If it is newly introduced, address it before merging.


⚠️ Low — E2E beforeEach skips waitForMendixApp after action button click

File: e2e/ComboboxMultiSelectionKeys.spec.js line 11–13
Note: The existing Combobox.spec.js calls await waitForMendixApp(page) after await page.click(".mx-name-actionButton1") because the button presumably opens a modal and triggers a Mendix re-render. The new spec omits it and immediately clicks .mx-name-tabPage2. Playwright's built-in auto-waiting on page.click and the per-test await expect(comboBox).toBeVisible({ timeout: 10000 }) guard mean this is unlikely to flake in practice — but if it does, adding await waitForMendixApp(page) between the two clicks (not after goto, which the fixture already handles) would be the fix.


Positives

  • The root cause analysis in the PR description is precise and matches the fix exactly — the mis-identification of a select-all range (selectionStart === 0, selectionEnd > 0) as "caret at position 0" is clearly described.
  • isChipNavigationPermitted faithfully mirrors downshift's internal isKeyDownOperationPermitted predicate, including the modifier-key guard, and the comment names the exact downshift version and source file for future maintainers. This prevents the widget and the library from diverging on who owns a keypress.
  • Unit tests cover every meaningful permutation: full-range selection, partial selection from 0, collapsed caret at end, collapsed caret at position 0 (the empty-input regression case), no chips, modifier key held, and the ArrowLeft/boxes-style path. All use RTL and existing builders — no Enzyme, no manual mocks.
  • E2E spec correctly imports from @mendix/run-e2e/fixtures (not raw Playwright), uses await expect(input).toHaveValue(...) web-first assertions instead of hardcoded waits, and re-verifies the cleared state after a blur/refocus cycle — which is exactly the customer-reported symptom.
  • CHANGELOG entry is user-facing ("we fixed an issue where…") with no implementation detail, following the repo convention.

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.

1 participant