[EuiComboBox] Pasting a delimited/copied list of values doesn't create multiple options
- Dominant language
- TypeScript
- Stars
- 6.4k
- Forks
- 911
- Avg merge
- 2d 11h
- Merged PRs (30d)
- 65
Description
### Summary
When `EuiComboBox` is given a `delimiter`, **pasting** a delimited (or copied multi-value) string does not reliably create one option per value. In common cases it collapses everything into a single option/pill. Copy→paste of a list of tags is a very frequent user expectation, so it would be great if `EuiComboBox` handled it natively when a `delimiter` is configured.
**EUI version:** `116.5.0`
**Component:** `EuiComboBox`
### Current behavior
Two separate problems combine to break paste:
1. **The search field is a single-line ``, which sanitizes pasted text before EUI sees it.**
In `combo_box_input.tsx` the input only receives values via `onChange(event.target.value)`. Per the HTML value-sanitization algorithm, a single-line `` strips/normalizes newlines. So when a user copies rendered tag badges (block-level elements, which land on the clipboard **newline-separated**) and pastes them, the newlines are gone (stripped, or collapsed to spaces depending on browser) *before* `onChange`/`onSearchChange` runs. The delimiter is destroyed and the whole thing becomes one value. Even a `delimiter=","` doesn't help, because the pasted text never contained commas.
2. **The `delimiter` split path calls `onCreateOption` once per value in a synchronous loop.**
In `combo_box.tsx`, `setCustomOptions` (called on Enter/blur, and from `onSearchChange` when the value `endsWith(delimiter)`) does:
```js
values.forEach((option) => addCustomOption(isContainerBlur, option));
```
Each `addCustomOption` calls `onCreateOption(value, ...)`. Consumers that derive the next state from a captured/controlled value (react-hook-form `field.onChange`, `useReducer` dispatch, `setState([...prev, x])` with a stale closure) end up dropping all but the last value, because the loop runs synchronously before any re-render.
### Expected behavior
When `delimiter` is provided, pasting a list of values should create **one option/pill per value** in a **single** update — including when the clipboard is newline-separated (the natural result of copying rendered pills/badges).
### Reproduction
1. Render an `EuiComboBox` with `delimiter=","`, `onCreateOption`, and a controlled `selectedOptions` (e.g. via react-hook-form).
2. Copy several rendered tags/badges (or any newline-separated list) to the clipboard.
3. Focus the combo box and paste, then blur/Enter.
**Actual:** a single option containing the concatenated text (e.g. `tag1tag2tag3` or `tag1 tag2 tag3`).
**Expected:** `tag1`, `tag2`, `tag3` as separate options.
### Proposal
- Add an `onPaste` handler in `EuiComboBoxInput` that, when `delimiter` is set, reads the **raw** `clipboardData.getData('text')` *before* the input sanitizes it, splits on the configured delimiter **and newlines**, and creates all resulting options in one pass (calling `preventDefault()`).
- To avoid the stale-closure problem when creating multiple options at once, either batch the created values and surface them together (e.g. a single `onCreateOption(values: string[])` / multi-create callback), or have EUI accumulate the created options internally before invoking the consumer once.
### Context
Real-world case: users copy tags from an existing item (rendered as badges) and paste them into a new item's tags field; they end up with one giant tag. We're currently working around this in Kibana by adding a per-consumer `onPaste` handler that reads the raw clipboard and splits on `/[\n\r,]+/`, but this really belongs in `EuiComboBox` when a `delimiter` is provided.
Contributor guide
Research direction
Start by reproducing the controlled selectedOptions case described in the issue, then inspect combo_box_input.tsx and combo_box.tsx, especially the paste, search-change, and setCustomOptions paths. Done means newline- or delimiter-separated clipboard values become separate options in one update without losing values for controlled consumers.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- react, typescript
- Domain
- frontend
- Issue type
- Bug
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Quiet
- Clarity
- Mostly clear
- Newbie friendliness
- 52/100