dequelabs / dequelabs/cauldron

Multiselect Combobox: no way to intercept or gate chip deselection

Open
#2,338 0 comments 0 reactions 0 assignees View on GitHub
Dominant language
TypeScript
Stars
127
Forks
31
Avg merge
2d 12h
Merged PRs (30d)
8

Description

## Problem

When a user clicks the × button on a chip in a multiselect Combobox, Cauldron applies the deselection to its internal state **before** firing `onSelectionChange`. This means consumers cannot:

- Show a confirmation dialog before the removal happens
- Conditionally prevent a deselection
- Gate removal behind any async operation

By the time `onSelectionChange` fires, the chip is already gone from the Combobox's internal state. The `value` prop is not re-read unless the component remounts.

## Use case

In axe-reports (dequelabs/axe-reports#3141), removing a dimension also removes access to all its subdimensions — a destructive action. We need to show a confirmation modal before allowing the removal.

## Current workaround

We force-remount the Combobox by changing its React `key` prop, which causes it to re-read the `value` prop (still containing the original selection) and restore the chip. Then we show the modal. If the user confirms, we update state and remount again. If they cancel, the chip is already restored.

```tsx
// In useRemovalConfirmation hook
const [comboboxResetKey, setComboboxResetKey] = useState(0);

const checkRemovalConfirmation = (dimension) => {
if (hasShownRef.current) return true; // skip modal

hasShownRef.current = true;
setPendingRemoval(dimension);
setComboboxResetKey(prev => prev + 1); // force remount to undo deselection
return false;
};

// In JSX

```

### Problems with this workaround

- **Loses Combobox internal state** — scroll position, typed filter text, open/close state are all reset on remount
- **Fragile** — depends on Cauldron re-reading `value` on mount and not caching `previousValue` from the old instance
- **Focus management complexity** — the remount destroys the DOM element that had focus, requiring deferred focus restoration via effects

## Suggested solutions (any of these would work)

1. **`onBeforeSelectionChange` callback** — fires before internal state changes, return `false` to prevent the deselection
2. **Fully controlled selection mode** — Combobox doesn't manage selection internally when `value` is provided as a controlled prop (similar to controlled `` in React)
3. **`confirmDeselection` prop** — accepts a callback `(removedValue: string) => Promise` that Cauldron awaits before applying the change

Option 2 (fully controlled mode) would be the most flexible and consistent with React conventions.

## Affected version

`@deque/cauldron-react` 6.18.5 (also verified on 6.27.0 — same behavior)

Contributor guide

Open the contributing guide

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.