elastic / elastic/eui

[EuiComboBox] Pasting a delimited/copied list of values doesn't create multiple options

Open
#9,806 3 comments 0 reactions 0 assignees View on GitHub
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

Open the contributing 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

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.