import { Meta } from "@storybook/addon-docs/blocks";

<Meta title="FP.REACT Components/Dialog/Readme" />

# Dialog Components

A modern, accessible dialog component system built with React and TypeScript, leveraging the native HTML `<dialog>` element for optimal performance and built-in accessibility features.

## Features

✅ **Controlled Component Pattern** - Full React state control with `isOpen`/`onOpenChange`
✅ **WCAG 2.1 AA Compliant** - Proper ARIA attributes, focus management, keyboard navigation
✅ **Native Dialog API** - Leverages browser's built-in focus trap and escape key handling
✅ **TypeScript Support** - Comprehensive type definitions with full IntelliSense
✅ **Two Modes** - Modal dialogs (overlay) and inline alert dialogs
✅ **Focus Management** - Automatic focus restoration to trigger element
✅ **Flexible API** - Controlled `Dialog` or uncontrolled `DialogModal` wrapper
✅ **Size Variants** - Small, medium, large, and full-screen dialog sizes
✅ **Position Control** - Center, top, bottom, left/right drawers, and corner positioning
✅ **Customizable** - CSS custom properties for theming
✅ **Keyboard Accessible** - `:focus-visible` styles, Escape key support
✅ **High Contrast Mode** - Supports `prefers-contrast: high` media query

---

## Component Architecture

The dialog system consists of:

| Component | Purpose | Usage |
|-----------|---------|-------|
| **`Dialog`** | Controlled modal/alert dialog | When you need full state control |
| **`DialogModal`** | Uncontrolled wrapper with trigger button | For simple use cases |
| **`DialogHeader`** | Header with title and close button | Internal (auto-rendered) |
| **`DialogFooter`** | Footer with confirm/cancel actions | Internal (auto-rendered) |

---

## Quick Start

### Option 1: Controlled Dialog (Recommended)

Use this when you need to control the dialog state from your component.

```tsx
import { Dialog } from "@fpkit/acss";
import { useState } from "react";

function MyComponent() {
  const [isOpen, setIsOpen] = useState(false);

  return (
    <>
      <button onClick={() => setIsOpen(true)}>Open Dialog</button>

      <Dialog
        isOpen={isOpen}
        onOpenChange={setIsOpen}
        dialogTitle="Confirm Action"
      >
        <p>Are you sure you want to proceed?</p>
      </Dialog>
    </>
  );
}
```

### Option 2: Uncontrolled DialogModal (Simple)

Use this when you want a dialog with a built-in trigger button and automatic state management.

```tsx
import { DialogModal } from "@fpkit/acss";

function MyComponent() {
  return (
    <DialogModal
      dialogTitle="Delete Item"
      btnLabel="Delete"
      btnSize="md"
      onConfirm={async () => {
        await deleteItem();
      }}
      confirmLabel="Delete"
      cancelLabel="Cancel"
    >
      <p>This action cannot be undone. Are you sure?</p>
    </DialogModal>
  );
}
```

---

## API Reference

### Dialog Props (Controlled)

#### Required Props

| Prop | Type | Description |
|------|------|-------------|
| `isOpen` | `boolean` | Controls whether the dialog is currently open |
| `onOpenChange` | `(open: boolean) => void` | Callback fired when dialog open state changes |
| `dialogTitle` | `string` | Title displayed in the dialog header |
| `children` | `ReactNode` | Content to display inside the dialog body |

#### Optional Props

| Prop | Type | Default | Description |
|------|------|---------|-------------|
| `isAlertDialog` | `boolean` | `false` | If true, renders as non-modal inline alert using `dialog.show()` |
| `onClose` | `() => void` | - | **Deprecated:** Use `onOpenChange`. Called when dialog closes. |
| `onConfirm` | `() => void \| Promise<void>` | - | Callback fired when confirm button is clicked |
| `confirmLabel` | `string` | `"Confirm"` | Text label for the confirm button |
| `cancelLabel` | `string` | `"Cancel"` | Text label for the cancel button |
| `hideFooter` | `boolean` | `false` | If true, hides the footer with action buttons |
| `className` | `string` | `""` | Additional CSS classes to apply to the dialog |
| `dialogLabel` | `string` | - | Optional `aria-label` for the dialog |
| `styles` | `CSSProperties` | - | Inline styles to apply to the dialog element |
| `size` | `"sm" \| "md" \| "lg" \| "full"` | - | Size variant controlling dialog dimensions |
| `position` | `"center" \| "top" \| "bottom" \| "left" \| "right" \| "top-left" \| "top-right" \| "bottom-left" \| "bottom-right"` | `"center"` | Position of the dialog on screen |

---

### DialogModal Props (Uncontrolled)

All `Dialog` props plus:

| Prop | Type | Default | Description |
|------|------|---------|-------------|
| `btnLabel` | `string` | `"Open Dialog"` | Text label for the trigger button |
| `btnSize` | `"sm" \| "md" \| "lg"` | `"sm"` | Size variant for the trigger button |
| `btnOnClick` | `() => void` | - | Callback fired when trigger button is clicked (before opening) |
| `btnProps` | `Record<string, unknown>` | - | Additional props to pass to the trigger button |

**Note:** `DialogModal` manages its own state internally, so you don't need `isOpen`/`onOpenChange`.

---

## Usage Examples

### Basic Modal Dialog

```tsx
import { Dialog } from "@fpkit/acss";
import { useState } from "react";

function BasicExample() {
  const [open, setOpen] = useState(false);

  return (
    <>
      <button onClick={() => setOpen(true)}>Show Info</button>

      <Dialog
        isOpen={open}
        onOpenChange={setOpen}
        dialogTitle="Information"
        hideFooter
      >
        <p>This is a simple informational dialog.</p>
        <p>Click the X or press Escape to close.</p>
      </Dialog>
    </>
  );
}
```

### Confirmation Dialog with Actions

```tsx
import { Dialog } from "@fpkit/acss";
import { useState } from "react";

function ConfirmationExample() {
  const [open, setOpen] = useState(false);

  const handleDelete = async () => {
    await deleteItem();
    setOpen(false);
  };

  return (
    <>
      <button onClick={() => setOpen(true)}>Delete Item</button>

      <Dialog
        isOpen={open}
        onOpenChange={setOpen}
        dialogTitle="Confirm Deletion"
        onConfirm={handleDelete}
        confirmLabel="Delete"
        cancelLabel="Keep"
      >
        <p>Are you sure you want to delete this item?</p>
        <p>This action cannot be undone.</p>
      </Dialog>
    </>
  );
}
```

### Inline Alert Dialog (Non-Modal)

```tsx
import { Dialog } from "@fpkit/acss";
import { useState } from "react";

function AlertExample() {
  const [showAlert, setShowAlert] = useState(false);

  return (
    <div>
      <button onClick={() => setShowAlert(true)}>Show Alert</button>

      {/* Non-modal: page remains interactive */}
      <Dialog
        isOpen={showAlert}
        onOpenChange={setShowAlert}
        dialogTitle="Warning"
        isAlertDialog={true}
        hideFooter
      >
        <p>⚠️ Your session will expire in 5 minutes.</p>
      </Dialog>

      <p>This content remains interactive even when alert is shown.</p>
    </div>
  );
}
```

### DialogModal with Simple API

```tsx
import { DialogModal } from "@fpkit/acss";

function SimpleExample() {
  return (
    <DialogModal
      dialogTitle="Subscribe to Newsletter"
      btnLabel="Subscribe"
      btnSize="lg"
      onConfirm={async () => {
        await subscribe();
      }}
      confirmLabel="Sign Up"
      cancelLabel="Maybe Later"
    >
      <p>Get weekly updates and exclusive content!</p>
      <input type="email" placeholder="your@email.com" />
    </DialogModal>
  );
}
```

### Size Variants

```tsx
import { DialogModal } from "@fpkit/acss";

function SizeExample() {
  return (
    <>
      {/* Small dialog (25rem) */}
      <DialogModal dialogTitle="Small" btnLabel="Open Small" size="sm">
        <p>A compact dialog for simple actions.</p>
      </DialogModal>

      {/* Medium dialog (32rem) */}
      <DialogModal dialogTitle="Medium" btnLabel="Open Medium" size="md">
        <p>A standard dialog for forms and content.</p>
      </DialogModal>

      {/* Large dialog (48rem) */}
      <DialogModal dialogTitle="Large" btnLabel="Open Large" size="lg">
        <p>A wide dialog for complex content.</p>
      </DialogModal>

      {/* Full-screen dialog */}
      <DialogModal dialogTitle="Full Screen" btnLabel="Open Full" size="full">
        <p>This dialog fills the entire viewport.</p>
      </DialogModal>
    </>
  );
}
```

### Position Variants

```tsx
import { DialogModal } from "@fpkit/acss";

function PositionExample() {
  return (
    <>
      {/* Top-positioned dialog */}
      <DialogModal dialogTitle="Top" btnLabel="Open Top" position="top">
        <p>Positioned at the top center of the viewport.</p>
      </DialogModal>

      {/* Bottom sheet */}
      <DialogModal dialogTitle="Bottom Sheet" btnLabel="Open Bottom" position="bottom">
        <p>Positioned at the bottom, like a mobile bottom sheet.</p>
      </DialogModal>

      {/* Right drawer (full-height side panel) */}
      <DialogModal dialogTitle="Right Drawer" btnLabel="Open Drawer" size="sm" position="right">
        <p>A full-height drawer sliding in from the right.</p>
      </DialogModal>

      {/* Corner positioning */}
      <DialogModal dialogTitle="Corner" btnLabel="Open Corner" size="sm" position="bottom-right">
        <p>Positioned in the bottom-right corner.</p>
      </DialogModal>
    </>
  );
}
```

### Custom Styling

```tsx
import { Dialog } from "@fpkit/acss";
import { useState } from "react";

function StyledExample() {
  const [open, setOpen] = useState(false);

  return (
    <Dialog
      isOpen={open}
      onOpenChange={setOpen}
      dialogTitle="Custom Styled Dialog"
      className="my-custom-dialog"
      styles={{ maxWidth: "600px", borderRadius: "1rem" }}
    >
      <p>This dialog has custom styles applied.</p>
    </Dialog>
  );
}
```

---

## Modal vs Non-Modal Behavior

### Modal Dialog (Default)

Uses `dialog.showModal()` which provides:
- ✅ Full-page overlay with backdrop
- ✅ Automatic focus trap (Tab cycles within dialog)
- ✅ Escape key closes dialog (native behavior)
- ✅ Backdrop click closes dialog
- ✅ Background becomes inert (non-interactive)

```tsx
<Dialog
  isOpen={open}
  onOpenChange={setOpen}
  dialogTitle="Modal Dialog"
>
  Content here - page is blocked
</Dialog>
```

### Inline Alert Dialog (`isAlertDialog={true}`)

Uses `dialog.show()` for non-modal inline alerts:
- ✅ Positioned inline in page flow
- ❌ No focus trap (page remains interactive)
- ❌ No escape key behavior
- ❌ No backdrop
- ✅ User can interact with page content

```tsx
<Dialog
  isOpen={showAlert}
  onOpenChange={setShowAlert}
  dialogTitle="Alert"
  isAlertDialog={true}
>
  Warning message - page stays interactive
</Dialog>
```

---

## Accessibility Features

### WCAG 2.1 AA Compliance

#### ARIA Attributes
- `aria-labelledby` - Links dialog to title using unique ID
- `aria-describedby` - Links dialog to content description
- `aria-modal="true"` - Indicates modal dialogs
- `aria-label` - Custom accessible label support
- `role="dialog"` or `role="alertdialog"` - Semantic roles

#### Keyboard Navigation
- **Escape key** - Closes modal dialogs (native `<dialog>` behavior)
- **Tab** - Cycles through focusable elements within dialog (focus trap)
- **Enter/Space** - Activates buttons
- `:focus-visible` styles for keyboard users

#### Focus Management
- **Auto-focus** - Dialog close button receives focus when opened
- **Focus restoration** - Focus returns to trigger button when closed
- **Focus trap** - Tab navigation stays within modal dialog

#### Screen Reader Support
- Semantic HTML structure
- Proper heading hierarchy
- Clear button labels
- Accessible close button ("Close dialog")

### High Contrast Mode

```scss
@media (prefers-contrast: high) {
  dialog {
    --dialog-border-color: currentColor;
    --dialog-border-width: 0.125rem;
    --dialog-focus-width: 0.1875rem;
  }
}
```

---

## Styling & Customization

### CSS Custom Properties

Override these variables to customize the dialog appearance:

```css
:root {
  /* Dimensions */
  --dialog-min-width: max(20rem, 80%);
  --dialog-width: var(--dialog-min-width);
  --dialog-max-width: 90vw;
  --dialog-height: auto;
  --dialog-max-height: 85vh;
  --dialog-margin: auto;
  --dialog-inset: 0;
  --dialog-gap: 0.625rem;
  --dialog-padding: 1.5rem;
  --dialog-padding-inline: 1rem;

  /* Borders */
  --dialog-border-color: lightgray;
  --dialog-border-width: thin;
  --dialog-border-radius: var(--border-radius);

  /* Buttons */
  --dialog-button-bg: transparent;
  --dialog-button-hover-bg: whitesmoke;
  --dialog-close-color: gray;

  /* Focus (Accessibility) */
  --dialog-focus-color: #0066cc;
  --dialog-focus-width: 0.125rem;
  --dialog-focus-offset: 0.125rem;

  /* Layout */
  --dialog-display: flex;
  --dialog-flex-direction: column;
}
```

### Example: Custom Theme

```scss
// Dark theme dialog
.dark-theme-dialog {
  --dialog-border-color: #444;
  --dialog-button-hover-bg: #333;
  --dialog-close-color: #ccc;
  background: #1a1a1a;
  color: #fff;
}
```

---

## TypeScript Support

### Import Types

```tsx
import type {
  DialogProps,
  DialogModalProps,
  DialogHeaderProps,
  DialogFooterProps,
  DialogSize,
  DialogPosition,
} from "@fpkit/acss";
```

### Type Definitions

All components are fully typed with comprehensive JSDoc comments:

```tsx
type DialogSize = "sm" | "md" | "lg" | "full";
type DialogPosition =
  | "center" | "top" | "bottom"
  | "left" | "right"
  | "top-left" | "top-right"
  | "bottom-left" | "bottom-right";

interface DialogProps extends BaseDialogProps {
  isOpen: boolean;
  onOpenChange: (open: boolean) => void;
  isAlertDialog?: boolean;
  onClose?: () => void; // Deprecated
  onConfirm?: () => void | Promise<void>;
  confirmLabel?: string;
  cancelLabel?: string;
  hideFooter?: boolean;
  size?: DialogSize;
  position?: DialogPosition;
}
```

---

## Migration Guide

### Upgrading from Old API

**Before (Old API):**
```tsx
<Dialog
  showDialog={isOpen}
  onClose={() => setIsOpen(false)}
  dialogTitle="Old API"
>
  Content
</Dialog>
```

**After (New API):**
```tsx
<Dialog
  isOpen={isOpen}
  onOpenChange={setIsOpen}
  dialogTitle="New API"
>
  Content
</Dialog>
```

**Backward Compatible (Temporary):**
```tsx
<Dialog
  isOpen={isOpen}
  onOpenChange={setIsOpen}
  onClose={() => console.log('Still works!')} // Deprecated but functional
  dialogTitle="Hybrid"
>
  Content
</Dialog>
```

### Breaking Changes

| Old Prop | New Prop | Migration |
|----------|----------|-----------|
| `showDialog` | `isOpen` | Rename prop |
| `onClose` | `onOpenChange` | Change signature from `() => void` to `(open: boolean) => void` |
| `dialogId` | *(removed)* | Use auto-generated IDs via `useId()` |

---

## Best Practices

### ✅ Do

```tsx
// Use controlled pattern for complex flows
const [step, setStep] = useState(1);
<Dialog isOpen={step === 2} onOpenChange={(open) => !open && setStep(1)}>
  Multi-step content
</Dialog>

// Provide clear button labels
<Dialog confirmLabel="Delete Forever" cancelLabel="Keep Item">

// Use DialogModal for simple cases
<DialogModal dialogTitle="Quick Action" btnLabel="Go">
```

### ❌ Don't

```tsx
// Don't manage state twice
const [open, setOpen] = useState(false);
<Dialog isOpen={open} onOpenChange={setOpen} onClose={() => setOpen(false)}>
  // onClose is redundant with onOpenChange

// Don't use inline alerts for critical actions
<Dialog isAlertDialog={true} onConfirm={deleteAllData}>
  // Use modal for destructive actions!

// Don't skip ARIA labels for custom content
<Dialog hideFooter>
  <button>Close</button> {/* Missing aria-label */}
</Dialog>
```

---

## Performance Considerations

The dialog component is optimized with:
- ✅ `React.memo` on subcomponents (DialogHeader, DialogFooter)
- ✅ `useCallback` for event handlers
- ✅ Minimal re-renders via proper dependency arrays
- ✅ Native `<dialog>` API (no JavaScript focus trap)

---

## Browser Support

Requires browsers with native `<dialog>` element support:
- ✅ Chrome 37+
- ✅ Edge 79+
- ✅ Firefox 98+
- ✅ Safari 15.4+

For older browsers, consider using a polyfill like [`dialog-polyfill`](https://github.com/GoogleChrome/dialog-polyfill).

---

## Testing

The dialog component has comprehensive test coverage:
- ✓ 40 tests covering all scenarios
- ✓ Controlled vs uncontrolled behavior
- ✓ Modal vs non-modal rendering
- ✓ ARIA attributes validation
- ✓ Focus restoration
- ✓ Keyboard interactions

Run tests:
```bash
npm test -- dialog.test.tsx
```

---

## Related Components

- **DialogHeader** - Header with title and close button (internal)
- **DialogFooter** - Footer with confirm/cancel buttons (internal)
- **Button** - Used for trigger and action buttons
- **Icon** - Used for close button icon

---

## Resources

- [MDN: `<dialog>` Element](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/dialog)
- [ARIA: Dialog Role](https://www.w3.org/WAI/ARIA/apg/patterns/dialog-modal/)
- [WCAG 2.1 Guidelines](https://www.w3.org/WAI/WCAG21/quickref/)

---

## License

Part of the `@fpkit/acss` component library.
