import { Meta, Canvas, Controls } from '@storybook/addon-docs/blocks'
import * as TooltipStories from './Tooltip.stories'

<Meta of={TooltipStories} />

# Tooltip

A 30px elevated pill with a caption, and **WithTooltip**, the wrapper that
shows it beside a trigger — on hover, and on keyboard focus.

<Canvas of={TooltipStories.OnHoverTop} />

## When

- The reason a control is disabled — a disabled control explains itself
  ("Available after sign-in"); this is the rule that an action appears
  only where it can succeed.
- The word for an icon-only control — alongside its `aria-label`, never
  instead of it (IconButton wires this for you via its `tooltip` prop).
- The full text behind a truncation.
- **The key that does the same thing** — pass `shortcut` and it is drawn as
  the `Kbd` chip after the label. For an icon-only control whose only other
  affordance is a keyboard shortcut, the tooltip is the only place to say so.
- A detail behind a short word in running text — the time behind "edited".

<Canvas of={TooltipStories.ShortcutComparison} />

<Canvas of={TooltipStories.OnADisabledControl} />

## When not

- Anything the person must read to proceed → put it in the surface, not
  behind a hover.
- Anything with controls inside → **Popover**, opened by a click. A list of
  actions → **Menu**. A tooltip disappears as the pointer moves onto it, so
  nothing inside it can be clicked.
- Something worth reading, scrolling or copying on hover → **PreviewCard**.

## How

```tsx
import { WithTooltip } from '@estiva-app/ui'

<WithTooltip label="Available after sign-in">
  <Button disabled>Publish</Button>
</WithTooltip>
```

- Wrap a **control, never a block** — the wrapper is `inline-flex` and
  shrinks nothing, so a wrapped block collapses to its content width.
- A truncating label keeps truncating if you pass
  `wrapperClassName="min-w-0 shrink"`.
- **`inline`** for a trigger inside a line of text — a reference, a name in a
  sentence: the wrapper is a `<span>`, because a `<div>` is not allowed inside
  a paragraph. It sits at the top of the line by itself, so the line keeps its
  height; add no wrapper of your own around it.
- A label longer than 320px wraps onto more lines rather than running off the
  screen; a short one stays one line at 30px.
- **`Button` and `IconButton` need no wrapper**: pass `tooltip` (or
  `disabledReason`) and the button itself becomes the trigger. That is what
  lets a `Dialog.Close` or a `Menu.Trigger` be one of them — the component's
  root is the `<button>`, with nothing in between.
- `shortcut` renders, it does not format. A modifier is called Cmd on Apple
  platforms and Ctrl elsewhere, and only the caller knows which it is
  looking at — so pass the finished label. `IconButton` forwards its own
  `tooltipShortcut` here.

<Canvas of={TooltipStories.InsideText} />

<Canvas of={TooltipStories.LongLabel} />

### The delay, and why a toolbar only pauses once

A tooltip waits **300ms** before it opens. Mount **one `TooltipProvider` at
the top of the app** and every tooltip below it shares that delay: the first
waits, and while the group stays warm the neighbours open as the pointer
arrives — so reading along a row of icon buttons is one pause, not one per
button. Without the provider nothing breaks; each simply waits its own 300ms.

```tsx
import { TooltipProvider } from '@estiva-app/ui'

<TooltipProvider>
  <App />
</TooltipProvider>
```

<Canvas of={TooltipStories.InAToolbar} />

### The motion

It fades in over **120ms** and travels **4px** away from the trigger — up
from below when it stands above a control, down from above when it hangs
beneath one — and fades out over **80ms**. Moving between triggers inside a
warm group skips the animation entirely, so a toolbar sweep does not flicker.
`prefers-reduced-motion` removes it.

## What it owns

It does these by itself. Write none of them around it.

| It does | So you never write | Refused when written by hand |
|---|---|---|
| Floats on top of the page | a portal, or a `z-index` to win | ✓ |
| Stays attached to its anchor on scroll and resize | a scroll or resize listener, or placement arithmetic | ✓ |
| Says what it is to assistive technology: a tooltip | `role="tooltip"` | ✓ |
| Is the tooltip: our look, our 300ms, one shared delay | a `title` attribute, the browser's grey box | ✓ (`estiva/no-native-title`) |
| Opens after the pointer rests, and at once on keyboard focus | a timer, or hover and focus handlers |  |
| Closes on Escape, on a click of its control, and when the pointer leaves | close handlers |  |

## Keys

| Input | What happens |
|---|---|
| pointer rests on the trigger | opens after 300ms — instantly if a neighbour's tooltip was just up |
| pointer leaves | closes |
| Tab onto the control | opens, with no delay |
| Tab away | closes |
| Escape while it is up | closes |
| a click on the trigger | closes it — the tooltip has said its piece |

## Props

<Controls of={TooltipStories.Default} />
