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

<Meta of={ModalStories} name="Guideline" />

# Modal

A modal interrupts the user to focus on a single task or decision. It blocks the rest of the UI until explicitly dismissed. Every modal has an action — there are no purely informational modals.

## When to use / When not to use

**✅ Use a modal for:**
- A confirmation before a consequential action (delete, reset, publish)
- A short, focused form that doesn't warrant a full page
- A required decision the user must address before continuing

**❌ Do not use a modal for:**
- Multi-step flows — use a full page or wizard instead
- Anything that would trigger on top of another modal
- Events triggered by background conditions (new version available, license missing) — race conditions between modals create a broken experience

## Size

- **Min-width:** 480px — never narrower than this regardless of content
- **Max-width:** 90vw — the modal adapts to its content between these two bounds
- Avoid scroll whenever possible. The modal keeps vertical padding against the viewport edges at all times. If the body content overflows, it scrolls with a visible scrollbar.

## Role: `dialog` vs `alertdialog`

The `role` prop controls how assistive technologies treat the modal.

| `role` | When to use | Close button |
|---|---|---|
| `dialog` (default) | Standard interaction — form, optional confirmation | Present |
| `alertdialog` | Critical or destructive action requiring a forced decision | Omit |

Use `alertdialog` when accidentally dismissing the modal would be dangerous or meaningless (e.g. a delete confirmation — ESC should not silently cancel the user's intent).

## Close button and backdrop

- The **close button** (×) in the header is present by default via the `close` prop. Omit it only when using `role="alertdialog"` — the user must explicitly choose an action.
- **Clicking the backdrop never closes the modal.** This prevents accidental data loss, especially in forms.
- **ESC** closes the modal when the `close` prop is provided, regardless of the role.

## Title and subtitle

- `title` is always required and always text. It mirrors the verb of the action that triggered the modal: the button "Delete node" opens a modal titled "Delete node?".
- For standard modals, the title describes the action: "Edit settings", "Create bucket".
- For destructive or irreversible actions, the title is phrased as a question: "Delete node?", "Revoke access?". The question mark signals that a decision is required.
- `subTitle` occupies the header slot when no close button is present. Use it for contextual information (e.g. a step indicator or a required-fields disclaimer).
- `subTitle` occupies the same slot as the close button. When `subTitle` is used, `close` must be omitted and navigation must be handled in the footer.

## Body content

- When the action targets a specific resource, name it explicitly in the body: **"my-node-name** will be permanently deleted, this action is irreversible." This prevents the user from acting on the wrong resource.
- For destructive or irreversible actions without a named resource, the body should still convey the consequence clearly.
- Links are allowed in the body. They must open in a new tab — the modal stays open.

## Footer and actions

- **Every modal must have a footer** with at least one action button. A modal without a footer is not valid.
- All buttons are **right-aligned** — the primary or danger action is the rightmost, the cancel/outline action sits immediately to its left.
- **2 buttons** is the norm. **3 buttons** is acceptable. **4 buttons** should be avoided.
- Footer is reserved for actions only — no links in the footer.
- Destructive action: `variant="danger"` — it replaces `primary`, never coexists with it.

<Canvas of={ModalStories.SimpleModal} layout="fullscreen" />

<Canvas of={ModalStories.DestructiveModal} layout="fullscreen" />

## Destructive and irreversible modals

Use `role="alertdialog"`, omit the close button, and use `variant="danger"` on the confirm action. Only the button carries the danger signal — the modal chrome does not change.

If the action targets a named resource, that name must appear in the body. If there is no named resource (e.g. resetting a configuration), it is optional.

## Async actions

| Operation duration | Pattern |
|---|---|
| < 3s | Use `isLoading` on the action button. Keep the modal open until resolved. |
| > 3s | Add a contextual message: "This may take a few moments." |
| Minutes or more | Do not use a modal. Use a background job with status feedback elsewhere in the UI. |

On error: stay in the modal and display the error inline. Do not close the modal silently. Do not rely on a toast as the only error signal.

## Forbidden patterns

- **No modal on top of another modal.** If a flow requires a second modal, redesign as a full page or wizard.
- **Avoid event-triggered modals** at the application level (login, WebSocket events, etc.) — race conditions between modals create an uncontrollable stacking experience.
- **No complex multi-step flows inside a modal.** Move to a full page. Exception: short download or export wizards.

## Within a table

Modals triggered from table row actions are valid. Use `size="inline"` for the trigger button.

<Canvas of={ModalStories.WithinTable} layout="fullscreen" />

## Playground

<Canvas of={ModalStories.SimpleModal} layout="fullscreen" />

<Controls of={ModalStories.SimpleModal} />
