Patterns

Attempts selector

An attempts filter for cards showing approval-rate / conversion metrics. "First attempts" ignores retries (only the first charge of each payment); "All attempts" includes retries in the denominator. Canonical placement is inside the ChartCard `actions` slot on cards where retry logic changes the interpretation of the metric.

Updated Aug 6, 2026 by Leonardo Posada

Anatomy

A DropdownMenu whose trigger is a small outline Button showing the currently selected label + a CaretDown. The menu has exactly two items: First attempts / All attempts. Controlled — the caller owns the state.

  1. 1
    Trigger

    shadcn <Button variant="outline" size="sm"> with the active label on the left and a CaretDown icon (Phosphor, weight light) on the right. h-8 default, rounded-md, uses the standard outline hover (bg-accent).

  2. 2
    Menu

    shadcn <DropdownMenuContent align="end"> anchored to the right edge of the trigger. Two <DropdownMenuItem>s that fire onChange with 'first' or 'all'.

  3. 3
    Labels (overridable)

    Defaults are the Yuno canon copy 'First attempts' / 'All attempts'. Pass `labels={{ first, all }}` to override for a specific locale. Prefer defaults unless you have a translation constraint.

Recipes

Standalone (outside a ChartCard)

Just the selector on its own. Rare on its own — it always attaches to something showing an approval-rate metric — but useful for a top-of-page filter row when the whole page's data reacts to First / All attempts.

const [value, setValue] = React.useState<AttemptsValue>("first");

<AttemptsSelector value={value} onChange={setValue} />
Inside ChartCard actions slot (canonical)

The canonical placement: inside the `actions` slot on cards where retry logic changes the interpretation of the metric (Daily cards approval rate by provider, Daily approval rate by provider + PM). Compatible with `viewPaymentsFilter` — the selector and the drilldown work independently.

Daily cards approval rate by provider

Chart body (multi-line series per provider)
<ChartCard
  title="Daily cards approval rate by provider"
  actions={<AttemptsSelector value={value} onChange={setValue} />}
>
  <MultiLineChart series={series} />
</ChartCard>
With custom labels

Override the default English labels via the `labels` prop. Only do this when the surface renders the selector in a non-English locale where a translation is mandatory — otherwise stay on defaults for consistency with the rest of the Yuno Dashboard.

<AttemptsSelector
  value={value}
  onChange={setValue}
  labels={{ first: "Solo primer intento", all: "Todos los intentos" }}
/>

Import

Copy this import line at the top of the file where you compose this molecule.

Standalone atom that renders a DropdownMenu with a shadcn outline Button trigger. Value is controlled — the caller owns state via `value` + `onChange`.
import { AttemptsSelector, type AttemptsValue } from "@/components/patterns/attempts-selector";

Props

Everything else is forwarded to the underlying elements via ...props.

PropTypeDefaultDescription
value'first' | 'all' (required)Current selection. Controlled — the caller owns state.
onChange(v: 'first' | 'all') => void (required)Fires when the user picks a different option.
labels{ first?: string; all?: string }Optional label overrides. Defaults to the Yuno canon copy ("First attempts" / "All attempts"). Use only when your surface renders the selector in a non-English locale.
classNamestringExtra classes on the trigger Button.

Related

Cross-links to the atoms this molecule composes and sibling patterns.

  • Chart cardCanonical host — the selector goes in the `actions` slot on approval-rate cards.
  • Dropdown menuThe underlying primitive. If you need a bigger menu or more options, drop the selector and compose DropdownMenu directly.
  • ButtonThe trigger is a shadcn <Button variant="outline" size="sm">. Match the same variant + size when placing controls next to it.