# xpl-input-search

Inner search field used by [`xpl-input`](../readme.md) when `type="search"`. It provides a leading magnifier, a native text field with `role="searchbox"`, an optional clear control, and a `shape` shell (`rounded` pill vs `box`). You normally consume search through **`xpl-input`**; use `xpl-input-search` only when composing a custom shell that still matches Apollo search visuals.

## Usage

### Recommended: `xpl-input` with `type="search"`

Default pill shell (`shape` defaults to `rounded`), placeholder, and optional `name` for forms:

```html
<xpl-input type="search" name="q" placeholder="Search products"></xpl-input>
```

Visible label, description, `box` shape, and `autocomplete`:

```html
<xpl-input
  type="search"
  label="Search catalog"
  description="SKU or product name"
  name="q"
  placeholder="SKU or name"
  shape="box"
  autocomplete="search"
></xpl-input>
```

When there is no visible label, set `aria-label` on `xpl-input` (forwarded to the inner field for assistive naming):

```html
<xpl-input type="search" aria-label="Search help topics" placeholder="Search…"></xpl-input>
```

Error state (border, `aria-invalid`, and error text on the parent) and an initial value (clear control appears when there is text):

```html
<xpl-input type="search" shape="box" error="Invalid query" value="abc"></xpl-input>
```

Listening for submit and live updates (vanilla JS):

```html
<xpl-input id="search" type="search" placeholder="Search"></xpl-input>
<script type="module">
  const el = document.getElementById('search');
  el.addEventListener('search', (e) => {
    console.log('submit', e.detail); // trimmed string
  });
  el.addEventListener('valueChange', (e) => {
    console.log('value', e.detail); // raw string
  });
</script>
```

### Events on `<xpl-input type="search">`

`valueChange`, `inputEvent`, `focusEvent`, and `blurEvent` are Stencil **`@Event`s** on **`xpl-input-search`** and **bubble** to the **`<xpl-input>`** host. **`focusEvent`** / **`blurEvent`** are also **re-emitted from `<xpl-input>`** so **`event.target`** matches **`type="text"`** (see **`xpl-input`** readme). **`valueChange`** / **`inputEvent`** are **not** re-emitted by the parent; use **`event.detail`** for the string if you do not want to depend on **`event.target`**.

The **commit** signal is a Stencil **`search`** **`@Event`** on **`xpl-input-search`** (trimmed query in **`detail`**). **`xpl-input`** wires **`onSearch`** on the inner tag, **`stopPropagation`s**, and **`this.search.emit(detail)`** so the public **`search`** **`@Event`** remains on **`<xpl-input>`**. **`setFocus()`** on **`<xpl-input>`** uses a **`ref`** to call the inner **`setFocus()`** only.

For advanced composition you can render `<xpl-input-search>` directly. You must pass **`input-id`** (matches a `<label for="…">` when used with a visible label). Set **`disabled`** and **`has-error`** (plus **`error-message-id`** when invalid) for both accessibility and visuals: the host adds `xpl-input-search--disabled` / `xpl-input-search--error` so disabled and error chrome match the wrapped `xpl-input` case. When nested under `xpl-input type="search"`, those props are set for you from the parent.

```html
<label class="xpl-input-label" for="sid">Search</label>
<xpl-input-search input-id="sid" name="q" placeholder="Search…"></xpl-input-search>
```

## Design tokens

| Part | Token |
| ---- | ----- |
| Host text (input inherits) | `--xpl-text-default` |
| Shell layout | `gap`: `--xpl-space-12`; horizontal padding: `--xpl-space-12`; `min-height` / `max-height`: `--xpl-size-250` |
| Shell fill (resting) | `--xpl-background-surface-default` |
| Shell `border-radius` (pill, default `shape="rounded"`) | `--xpl-border-radius-button` |
| Shell `border-radius` (`shape="box"`) | `--xpl-border-radius-small` |
| Shell border (resting) | `--xpl-border-default` |
| Shell border (input focus, empty) | `--xpl-border-active` |
| Shell border (input focus, with value) | `--xpl-border-focus` |
| Shell border (error, from parent `xpl-input`) | `--xpl-border-negative` |
| Disabled shell (parent) | `--xpl-background-action-disabled` |
| Input typography | `--xpl-font-family-default`, `--xpl-font-size-body`, `--xpl-font-weight-normal` |
| Placeholder | `--xpl-text-subdued` |
| Disabled text / placeholder (parent) | `--xpl-text-disabled` |
| Leading icon | `--xpl-icon-default` / `--xpl-icon-disabled` |
| Clear icon | `--xpl-icon-subtle` |
| Caret (focus, with value) | `--xpl-border-focus` |
| Caret (focus, error, parent) | `--xpl-border-negative` |

Light and dark mode use Foundation purpose-specific tokens only (no component `.dark` overrides).

## Icon mapping

| Role | When | `xpl-icon` name | `size` |
| ---- | ---- | ----------------- | ------ |
| Leading search | Always (when the control renders) | `magnifier` | 20 |
| Clear | Value non-empty and not `disabled` | `circle-xmark` | 16 |

## Accessibility

- **Role and naming:** The field uses a native `<input type="text">` with `role="searchbox"`. `type="text"` avoids browser search-field chrome that would clash with the design-system clear control. When the parent does not render a visible label, `aria-label` on the inner input defaults to `"Search"` unless `ariaLabel` / parent `aria-label` overrides it. With a visible label, the input is named via `<label for="…">` and redundant `aria-label` on the input is omitted.
- **`autocomplete`:** Default `off` is typical for inline search; set `autocomplete` on `xpl-input` for search fields when you need a specific token.
- **Errors:** With `xpl-input`, non-empty `error` sets `aria-invalid`, links the message with `aria-describedby`, and styles the shell from the parent host.
- **Keyboard — standalone `xpl-input-search`:** An inner `<form>` wraps the shell when the host is not already inside a `<form>`. **Enter** submits that inner form (default prevented); the host emits a Stencil **`search`** event (trimmed value in **`detail`**). Listen with **`addEventListener('search', …)`** on **`xpl-input-search`**. **Tab** visits the input and the clear control when visible.
- **Keyboard — inside a parent `<form>`:** The inner `<form>` is omitted. **Enter** uses native form submission; listen on the parent form’s **`submit`**. Use **`valueChange`** / **`inputEvent`** for live typing.
- **Clear control:** The clear button exposes **`aria-label="Clear search"`**. Activating it clears the value, emits **`valueChange`** / **`inputEvent`** with `''`, and refocuses the input. When **`disabled`** or **`readonly`** is true, the clear control is hidden and inert (no value mutation).

<!-- Auto Generated Below -->


## Properties

| Property               | Attribute           | Description                                                                                                                                                                                                                                                                                                                               | Type                 | Default     |
| ---------------------- | ------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------- | ----------- |
| `ariaLabel`            | `aria-label`        | Accessible name when there is no visible label from the parent; maps to `aria-label` on the input. When the parent provides a visible label, the input is named by `<label for="…">` — this prop is ignored for naming. Empty or whitespace-only values are treated as missing and fall back to `'Search'` so the field is never unnamed. | `string`             | `'Search'`  |
| `autocomplete`         | `autocomplete`      | Passed to the native input (`autocomplete` attribute). Default `off` matches common inline-search UX; browsers may still apply heuristics.                                                                                                                                                                                                | `string`             | `'off'`     |
| `disabled`             | `disabled`          | Disables the field and clear control.                                                                                                                                                                                                                                                                                                     | `boolean`            | `false`     |
| `errorMessageId`       | `error-message-id`  | Element id for `aria-describedby` when `hasError` (error message lives on `xpl-input`).                                                                                                                                                                                                                                                   | `string`             | `undefined` |
| `hasError`             | `has-error`         | Set by `xpl-input` when `error` is shown — drives `aria-invalid` / `aria-describedby`.                                                                                                                                                                                                                                                    | `boolean`            | `false`     |
| `hasVisibleLabel`      | `has-visible-label` | When true, parent `xpl-input` renders a visible label — omit `aria-label` on the input.                                                                                                                                                                                                                                                   | `boolean`            | `false`     |
| `inputId` _(required)_ | `input-id`          | Associates the control with the parent `xpl-input` label (`for` / `id`).                                                                                                                                                                                                                                                                  | `string`             | `undefined` |
| `name`                 | `name`              | Form field `name` on the native input.                                                                                                                                                                                                                                                                                                    | `string`             | `undefined` |
| `placeholder`          | `placeholder`       | Placeholder shown when the value is empty.                                                                                                                                                                                                                                                                                                | `string`             | `'Search'`  |
| `readonly`             | `readonly`          | Whether the input is editable.                                                                                                                                                                                                                                                                                                            | `boolean`            | `false`     |
| `required`             | `required`          | Whether the input is required.                                                                                                                                                                                                                                                                                                            | `boolean`            | `false`     |
| `shape`                | `shape`             | Shell corner radius: `rounded` (pill) or `box` (rounded rectangle).                                                                                                                                                                                                                                                                       | `"box" \| "rounded"` | `'rounded'` |
| `value`                | `value`             | Current value (controlled-friendly).                                                                                                                                                                                                                                                                                                      | `string`             | `''`        |


## Events

| Event         | Description                                                                                                                                                                                                                                                                                                        | Type                      |
| ------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ------------------------- |
| `blurEvent`   | Emitted when the inner control loses focus. When nested under `xpl-input type="search"`, this event bubbles to the `xpl-input` host (same event name as other input types).                                                                                                                                        | `CustomEvent<FocusEvent>` |
| `focusEvent`  | Emitted when the inner control receives focus. When nested under `xpl-input type="search"`, this event bubbles to the `xpl-input` host (same event name as other input types).                                                                                                                                     | `CustomEvent<FocusEvent>` |
| `inputEvent`  | Fired on every input change (typing, paste) and when the value is cleared — mirrors `xpl-input` text fields. When nested under `xpl-input type="search"`, bubbles to the `xpl-input` host.                                                                                                                         | `CustomEvent<string>`     |
| `search`      | Emitted when the user commits a search (Enter with no enclosing form owner, or submit of the inner search form). `event.detail` is the trimmed query string. When nested under `xpl-input type="search"`, the parent listens with `onSearch` and re-emits so the canonical `search` `@Event` stays on `xpl-input`. | `CustomEvent<string>`     |
| `valueChange` | Fired on every input change (typing, paste) and when the value is cleared. `event.detail` is the raw `value` string (not trimmed). When nested under `xpl-input type="search"`, bubbles to the `xpl-input` host.                                                                                                   | `CustomEvent<string>`     |


## Methods

### `setFocus() => Promise<void>`

Moves keyboard focus to the search field.

#### Returns

Type: `Promise<void>`




## Dependencies

### Used by

 - [xpl-input](..)

### Depends on

- [xpl-icon](../../xpl-icon)

### Graph
```mermaid
graph TD;
  xpl-input-search --> xpl-icon
  xpl-input --> xpl-input-search
  style xpl-input-search fill:#f9f,stroke:#333,stroke-width:4px
```

----------------------------------------------

*Built with [StencilJS](https://stenciljs.com/)*
