# PickerTriggerButton

> The compact 32px button that opens a Select or Combobox as a filter above a list, outlined until the filter is applied and filled once it is.

Source: https://raft-design.botiverse.build/components/picker-trigger-button. Generated from the current release; where it differs from what you remember, this page is right.

PickerTriggerButton is the button of a filter: a compact 32px trigger that opens the list of a [Select](https://raft-design.botiverse.build/components/select.md) or a [Combobox](https://raft-design.botiverse.build/components/combobox.md) above the list it filters. It has no state of its own. It is rendered as the select's or combobox's trigger, which keeps opening, focus, keyboard navigation and selection.

```tsx
import { ArrowDownUp, ChevronDown } from "lucide-react";
import {
  PickerTriggerButton,
  Select,
  SelectContent,
  SelectIcon,
  SelectItem,
  SelectItemIndicator,
  SelectItemText,
  SelectList,
  SelectTrigger,
  SelectValue,
} from "@botiverse/raft-ui";

const SORT_OPTIONS = [
  { value: "relevance", label: "Relevance" },
  { value: "recent", label: "Most recent" },
  { value: "comments", label: "Most commented" },
] as const;

export function PickerTriggerButtonSelectFilterComposition() {
  return (
    <Select defaultValue="relevance" items={SORT_OPTIONS} modal={false}>
      <SelectTrigger render={<PickerTriggerButton />}>
        <ArrowDownUp aria-hidden className="size-3.5" />
        <SelectValue />
        <SelectIcon>
          <ChevronDown aria-hidden className="size-3" />
        </SelectIcon>
      </SelectTrigger>
      <SelectContent className="w-[180px]">
        <SelectList>
          {SORT_OPTIONS.map((option) => (
            <SelectItem key={option.value} value={option.value}>
              <SelectItemText>{option.label}</SelectItemText>
              <SelectItemIndicator />
            </SelectItem>
          ))}
        </SelectList>
      </SelectContent>
    </Select>
  );
}
```

## When to use

| Situation                                     | Use                                                                     |
| --------------------------------------------- | ----------------------------------------------------------------------- |
| Filtering a list by one value or a few values | PickerTriggerButton, rendered as a `SelectTrigger` or `ComboboxTrigger` |
| Picking a value in a form                     | The select's own field ([Select](https://raft-design.botiverse.build/components/select.md))                  |
| A list of actions                             | A menu ([DropdownMenu](https://raft-design.botiverse.build/components/dropdown-menu.md))              |
| A row of options, each on or off on its own   | [ToggleGroup](https://raft-design.botiverse.build/components/toggle-group.md)                          |

The full choice between pickers is in [Choosing a component](https://raft-design.botiverse.build/components.md#picking-something). How a filter looks, how many values it shows and where Clear All goes are in [Select › Filters](https://raft-design.botiverse.build/components/select.md#filters).

## Anatomy

PickerTriggerButton is one part. Pass it as `render` to `SelectTrigger` or `ComboboxTrigger`, and put the trigger's usual parts inside, in this order:

- A leading icon: optional, 14px.
- The value: `SelectValue`, or the chosen label for a combobox. Required.
- `ComboboxTriggerCount`: how many values an applied combobox filter has. Optional.
- The indicator: `SelectIcon` or `ComboboxTriggerIndicator`, a chevron in `foreground-hint`.

```tsx
import { ChevronDown, Hash } from "lucide-react";
import { useState } from "react";
import {
  Combobox,
  ComboboxContent,
  ComboboxEmpty,
  ComboboxHeader,
  ComboboxInput,
  ComboboxInputGroup,
  ComboboxItem,
  ComboboxItemIndicator,
  ComboboxLabel,
  ComboboxList,
  ComboboxSeparator,
  ComboboxTrigger,
  ComboboxTriggerIndicator,
  PickerTriggerButton,
} from "@botiverse/raft-ui";

const CHANNELS = [
  { id: "design", label: "#design" },
  { id: "engineering", label: "#engineering" },
  { id: "product", label: "#product" },
] as const;

type Channel = (typeof CHANNELS)[number];

function channelLabel(item: Channel) {
  return item.label;
}

export function PickerTriggerButtonComboboxFilterComposition() {
  const [channel, setChannel] = useState<Channel | null>(CHANNELS[0]);

  return (
    <Combobox<Channel>
      items={CHANNELS}
      value={channel}
      itemToStringLabel={channelLabel}
      onValueChange={setChannel}
      autoHighlight
    >
      <ComboboxTrigger render={<PickerTriggerButton />}>
        <Hash aria-hidden className="size-3.5" />
        <span>{channel?.label ?? "Channel"}</span>
        <ComboboxTriggerIndicator>
          <ChevronDown aria-hidden className="size-3" />
        </ComboboxTriggerIndicator>
      </ComboboxTrigger>
      <ComboboxContent initialFocus={false} finalFocus={false} className="w-[180px]">
        <ComboboxHeader>
          <ComboboxLabel>Channel</ComboboxLabel>
        </ComboboxHeader>
        <ComboboxSeparator />
        <ComboboxInputGroup>
          <ComboboxInput aria-label="Search channels" placeholder="Search..." />
        </ComboboxInputGroup>
        <ComboboxList>
          {(item: Channel) => (
            <ComboboxItem key={item.id} value={item}>
              <span className="truncate">{item.label}</span>
              <ComboboxItemIndicator />
            </ComboboxItem>
          )}
        </ComboboxList>
        <ComboboxEmpty>No channels found</ComboboxEmpty>
      </ComboboxContent>
    </Combobox>
  );
}
```

## States

| State                      | Look                                                                                                                                                |
| -------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------- |
| Not applied                | `foreground-muted` text inside a `line-seam` outline                                                                                                |
| Applied (`data-has-value`) | `info-soft` fill, `foreground` text, no outline ([States](https://raft-design.botiverse.build/foundations/states.md#usage): an active filter is selected content) |
| Invalid (`data-invalid`)   | A `danger-strong` outline                                                                                                                           |
| Disabled                   | Follows [States](https://raft-design.botiverse.build/foundations/states.md#usage): `fill-muted`, `foreground-disabled` text                                       |

- `ComboboxTrigger` sets `data-has-value` when the combobox has a value. A `SelectTrigger` always holds a value, so it never sets it: pass `data-has-value` yourself when the filter differs from its default.

## Behavior

- The list opens next to the trigger, as every popup does ([Elevation](https://raft-design.botiverse.build/foundations/elevation.md#popups)); its width and alignment are in [Select › Behavior](https://raft-design.botiverse.build/components/select.md#behavior).
- The trigger keeps its height and doesn't wrap or truncate: a long value makes it wider.

## Content

- Show the current value, not the filter's name, once a value is chosen: Relevance, #launch-week. Before that, show the filter's name: Channel, Assignee.
- The general rules for labels are in [Writing](https://raft-design.botiverse.build/content/writing.md#casing).

## Accessibility

- PickerTriggerButton wraps the Base UI Button. The select or combobox it is rendered for adds the trigger's role, `aria-expanded` and the popup relationship.
- Its accessible name is its text. When the text is only the value (Relevance), add an `aria-label` that also names the filter ("Sort: Relevance"), because the value alone doesn't say what it changes.
- Hide the leading icon and the chevron from assistive technology (`aria-hidden`).
- Name the search row of a combobox filter with `aria-label` ("Search channels").

## Rules

- Use PickerTriggerButton only for filters. A picker in a form uses the select's own field with an ink border, as high as the inputs around it.
- Don't open a menu of actions from it: it always opens a list of values.
- Don't style an applied filter yourself. Set `data-has-value` and let the component fill it.

## API

### PickerTriggerButton

| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `focusableWhenDisabled` | `boolean` | `false` | Whether the button should be focusable when disabled. |
| `nativeButton` | `boolean` | `true` | Whether the component renders a native `<button>` element when replacing it via the `render` prop. Set to `false` if the rendered element is not a button (for example, `<div>`). |
| `render` | `ReactElement \| ((props, state) => ReactElement)` |  | Allows you to replace the component's HTML element with a different tag, or compose it with another component. |

Also takes the attributes of its element.

## Changes

- 0.5.0 (Added): Graduated `PickerTriggerButton` for compact Select and Combobox filter triggers. ([#208](https://github.com/botiverse/rui/pull/208), [0fef4c1](https://github.com/botiverse/rui/commit/0fef4c1d39e18335d2c2fc3a7102054f6f72339d))
