Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
62 changes: 62 additions & 0 deletions apps/www/src/components/playground/alert-dialog-examples.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
'use client';

import { AlertDialog, Button, Flex } from '@raystack/apsara';
import PlaygroundLayout from './playground-layout';

export function AlertDialogExamples() {
return (
<PlaygroundLayout title='AlertDialog'>
<Flex gap='medium'>
<AlertDialog>
<AlertDialog.Trigger render={<Button color='danger' />}>
Delete Item
</AlertDialog.Trigger>
<AlertDialog.Content width='400px'>
<AlertDialog.Header>
<AlertDialog.Title>Are you sure?</AlertDialog.Title>
</AlertDialog.Header>
<AlertDialog.Body>
<AlertDialog.Description>
This action cannot be undone. This will permanently delete the
item from our servers.
</AlertDialog.Description>
</AlertDialog.Body>
<AlertDialog.Footer>
<AlertDialog.Close
render={
<Button variant='outline' color='neutral'>
Cancel
</Button>
}
/>
<Button color='danger'>Delete</Button>
</AlertDialog.Footer>
</AlertDialog.Content>
</AlertDialog>
<AlertDialog>
<AlertDialog.Trigger render={<Button variant='outline' />}>
Discard Changes
</AlertDialog.Trigger>
<AlertDialog.Content width={400} showCloseButton={false}>
<AlertDialog.Body>
<AlertDialog.Title>Unsaved Changes</AlertDialog.Title>
<AlertDialog.Description>
You have unsaved changes. Do you want to discard them?
</AlertDialog.Description>
</AlertDialog.Body>
<AlertDialog.Footer>
<AlertDialog.Close
render={
<Button variant='outline' color='neutral'>
Continue Editing
</Button>
}
/>
<Button color='danger'>Discard</Button>
</AlertDialog.Footer>
</AlertDialog.Content>
</AlertDialog>
</Flex>
</PlaygroundLayout>
);
}
1 change: 1 addition & 0 deletions apps/www/src/components/playground/index.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
export * from './alert-dialog-examples';
export * from './amount-examples';
export * from './announcement-bar-examples';
export * from './avatar-examples';
Expand Down
175 changes: 175 additions & 0 deletions apps/www/src/content/docs/components/alert-dialog/demo.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,175 @@
'use client';

export const getCode = (props: { title?: string; description?: string }) => {
const { title, description } = props;
return `
<AlertDialog>
<AlertDialog.Trigger render={<Button color="danger" />}>
Discard draft
</AlertDialog.Trigger>
<AlertDialog.Content width={400} showCloseButton={false}>
<AlertDialog.Body>
<AlertDialog.Title>${title}</AlertDialog.Title>
<AlertDialog.Description>
${description}
</AlertDialog.Description>
</AlertDialog.Body>
<AlertDialog.Footer>
<AlertDialog.Close render={<Button variant="outline" color="neutral">Cancel</Button>} />
<AlertDialog.Close render={<Button color="danger">Discard</Button>} />
</AlertDialog.Footer>
</AlertDialog.Content>
</AlertDialog>`;
};
Comment on lines +3 to +23
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Function signature mismatch with GetCodeType interface.

The getCode function accepts one parameter, but the playground infrastructure calls it with two: getCode(updatedProps, componentProps) (see demo-playground.tsx lines 66-75). The GetCodeType interface in types.ts expects (updatedProps: Record<string, any>, props: Record<string, any>) => string.

Currently, props receives updatedProps (only changed values), not the full props object with defaults. If a user doesn't modify title or description, they won't be in updatedProps, causing undefined to appear in the rendered code.

Proposed fix to match the expected signature
-export const getCode = (props: { title?: string; description?: string }) => {
-  const { title, description } = props;
+export const getCode = (
+  updatedProps: Record<string, any>,
+  props: Record<string, any>
+) => {
+  const { title, description } = props;
   return `
     <AlertDialog>
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
export const getCode = (props: { title?: string; description?: string }) => {
const { title, description } = props;
return `
<AlertDialog>
<AlertDialog.Trigger render={<Button color="danger" />}>
Discard draft
</AlertDialog.Trigger>
<AlertDialog.Content width={400} showCloseButton={false}>
<AlertDialog.Body>
<AlertDialog.Title>${title}</AlertDialog.Title>
<AlertDialog.Description>
${description}
</AlertDialog.Description>
</AlertDialog.Body>
<AlertDialog.Footer>
<AlertDialog.Close render={<Button variant="outline" color="neutral">Cancel</Button>} />
<AlertDialog.Close render={<Button color="danger">Discard</Button>} />
</AlertDialog.Footer>
</AlertDialog.Content>
</AlertDialog>`;
};
export const getCode = (
updatedProps: Record<string, any>,
props: Record<string, any>
) => {
const { title, description } = props;
return `
<AlertDialog>
<AlertDialog.Trigger render={<Button color="danger" />}>
Discard draft
</AlertDialog.Trigger>
<AlertDialog.Content width={400} showCloseButton={false}>
<AlertDialog.Body>
<AlertDialog.Title>${title}</AlertDialog.Title>
<AlertDialog.Description>
${description}
</AlertDialog.Description>
</AlertDialog.Body>
<AlertDialog.Footer>
<AlertDialog.Close render={<Button variant="outline" color="neutral">Cancel</Button>} />
<AlertDialog.Close render={<Button color="danger">Discard</Button>} />
</AlertDialog.Footer>
</AlertDialog.Content>
</AlertDialog>`;
};
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/www/src/content/docs/components/alert-dialog/demo.ts` around lines 3 -
23, getCode currently accepts a single props object but the playground calls
getCode(updatedProps, componentProps) per the GetCodeType signature; update
getCode to accept two parameters (updatedProps: Record<string, any>, props:
Record<string, any>) and merge them (e.g., create a finalProps where keys from
props provide defaults for any missing in updatedProps) before using
title/description, ensuring the returned string never interpolates undefined
values; reference the getCode function and the title/description usages inside
the template and match the GetCodeType signature.


export const playground = {
type: 'playground',
controls: {
title: { type: 'text', initialValue: 'Discard draft?' },
description: {
type: 'text',
initialValue: "You can't undo this action."
}
},
getCode
};

export const controlledDemo = {
type: 'code',
code: `
function ControlledAlertDialog() {
const [open, setOpen] = React.useState(false);

return (
<AlertDialog open={open} onOpenChange={setOpen}>
<AlertDialog.Trigger render={<Button color="danger" />}>
Delete Account
</AlertDialog.Trigger>
<AlertDialog.Content width={450} showCloseButton={false}>
<AlertDialog.Header>
<AlertDialog.Title>Delete Account</AlertDialog.Title>
</AlertDialog.Header>
<AlertDialog.Body>
<AlertDialog.Description>
Are you sure you want to delete your account? All of your data
will be permanently removed. This action cannot be undone.
</AlertDialog.Description>
</AlertDialog.Body>
<AlertDialog.Footer>
<AlertDialog.Close render={<Button variant="outline" color="neutral">Cancel</Button>} />
<Button color="danger" onClick={() => setOpen(false)}>Yes, delete account</Button>
</AlertDialog.Footer>
</AlertDialog.Content>
</AlertDialog>
);
}`
};

export const menuDemo = {
type: 'code',
code: `
function MenuWithAlertDialog() {
const [dialogOpen, setDialogOpen] = React.useState(false);

return (
<React.Fragment>
<Menu>
<Menu.Trigger render={<Button variant="outline" />}>
Actions
</Menu.Trigger>
<Menu.Content>
<Menu.Item>Edit</Menu.Item>
<Menu.Item>Duplicate</Menu.Item>
<Menu.Separator />
<Menu.Item onClick={() => setDialogOpen(true)}>
Delete
</Menu.Item>
</Menu.Content>
</Menu>

<AlertDialog open={dialogOpen} onOpenChange={setDialogOpen}>
<AlertDialog.Content width={400} showCloseButton={false}>
<AlertDialog.Body>
<AlertDialog.Title>Delete item?</AlertDialog.Title>
<AlertDialog.Description>
This will permanently delete the item. You can't undo this action.
</AlertDialog.Description>
</AlertDialog.Body>
<AlertDialog.Footer>
<AlertDialog.Close render={<Button variant="outline" color="neutral">Cancel</Button>} />
<Button color="danger" onClick={() => setDialogOpen(false)}>Delete</Button>
</AlertDialog.Footer>
</AlertDialog.Content>
</AlertDialog>
</React.Fragment>
);
}`
};

export const discardDemo = {
type: 'code',
code: `
<AlertDialog>
<AlertDialog.Trigger render={<Button variant="outline" />}>
Discard Changes
</AlertDialog.Trigger>
<AlertDialog.Content width={400} showCloseButton={false}>
<AlertDialog.Header>
<AlertDialog.Title>Unsaved Changes</AlertDialog.Title>
</AlertDialog.Header>
<AlertDialog.Body>
<AlertDialog.Description>
You have unsaved changes. Do you want to discard them or continue editing?
</AlertDialog.Description>
</AlertDialog.Body>
<AlertDialog.Footer>
<AlertDialog.Close render={<Button variant="outline" color="neutral">Continue Editing</Button>} />
<AlertDialog.Close render={<Button color="danger">Discard</Button>} />
</AlertDialog.Footer>
</AlertDialog.Content>
</AlertDialog>`
};

export const nestedDemo = {
type: 'code',
code: `
function NestedAlertDialogExample() {
return (
<AlertDialog>
<AlertDialog.Trigger render={<Button color="danger" />}>
Delete Workspace
</AlertDialog.Trigger>
<AlertDialog.Content width={450}>
<AlertDialog.Header>
<AlertDialog.Title>Delete Workspace</AlertDialog.Title>
</AlertDialog.Header>
<AlertDialog.Body>
<AlertDialog.Description>
This will delete the workspace and all its projects. Are you sure?
</AlertDialog.Description>
<AlertDialog>
<AlertDialog.Trigger render={<Button color="danger" size="small" />}>
Confirm Delete
</AlertDialog.Trigger>
<AlertDialog.Content width={400} showCloseButton={false}>
<AlertDialog.Body>
<AlertDialog.Title>Final Confirmation</AlertDialog.Title>
<AlertDialog.Description>
This is your last chance. This action is permanent and cannot be reversed.
</AlertDialog.Description>
</AlertDialog.Body>
<AlertDialog.Footer>
<AlertDialog.Close render={<Button variant="outline" color="neutral">Go Back</Button>} />
<AlertDialog.Close render={<Button color="danger">Delete Everything</Button>} />
</AlertDialog.Footer>
</AlertDialog.Content>
</AlertDialog>
</AlertDialog.Body>
<AlertDialog.Footer>
<AlertDialog.Close render={<Button variant="outline" color="neutral">Cancel</Button>} />
</AlertDialog.Footer>
</AlertDialog.Content>
</AlertDialog>
);
}`
};
123 changes: 123 additions & 0 deletions apps/www/src/content/docs/components/alert-dialog/index.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,123 @@
---
title: AlertDialog
description: A modal dialog that interrupts the user with important content and expects a response. Unlike Dialog, it does not close on outside click, requiring explicit user action.
source: packages/raystack/components/alert-dialog
tag: new
---

import { playground, controlledDemo, menuDemo, discardDemo, nestedDemo } from "./demo.ts";

<Demo data={playground} />

## Anatomy

Import and assemble the component:

```tsx
import { AlertDialog } from '@raystack/apsara'

<AlertDialog>
<AlertDialog.Trigger />
<AlertDialog.Content>
<AlertDialog.Header>
<AlertDialog.Title />
</AlertDialog.Header>
<AlertDialog.Body>
<AlertDialog.Description />
</AlertDialog.Body>
<AlertDialog.Footer>
<AlertDialog.Close />
</AlertDialog.Footer>
</AlertDialog.Content>
</AlertDialog>
```

## API Reference

### Root

Groups all parts of the alert dialog.

<auto-type-table path="./props.ts" name="AlertDialogProps" />

### Content

Renders the alert dialog panel overlay.

<auto-type-table path="./props.ts" name="AlertDialogContentProps" />

### Header

Renders the alert dialog header section.

<auto-type-table path="./props.ts" name="AlertDialogHeaderProps" />

### Title

Renders the alert dialog title text.

<auto-type-table path="./props.ts" name="AlertDialogTitleProps" />

### Body

Renders the main content area of the alert dialog.

<auto-type-table path="./props.ts" name="AlertDialogBodyProps" />

### Description

Renders supplementary text within the alert dialog body.

<auto-type-table path="./props.ts" name="AlertDialogDescriptionProps" />

### Trigger

Renders the element that opens the alert dialog.

<auto-type-table path="./props.ts" name="AlertDialogTriggerProps" />

### CloseButton

Renders a button that closes the alert dialog.

<auto-type-table path="./props.ts" name="AlertDialogCloseButtonProps" />

### Footer

Renders the alert dialog footer section.

<auto-type-table path="./props.ts" name="AlertDialogFooterProps" />
Comment on lines +73 to +89
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

AlertDialog.Close is missing from the API reference.

The anatomy block and packages/raystack/components/alert-dialog/alert-dialog.tsx both expose AlertDialog.Close, but the reference only documents CloseButton. That leaves the main action-wrapping close primitive undocumented.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/www/src/content/docs/components/alert-dialog/index.mdx` around lines 73
- 89, The docs are missing the AlertDialog.Close API entry; update the MDX to
document the exported Close primitive by adding an API reference for
AlertDialog.Close (matching the symbol in
packages/raystack/components/alert-dialog/alert-dialog.tsx). Add an
auto-type-table or manual prop table for the Close primitive (e.g.,
AlertDialogCloseProps or the exact exported type name used in alert-dialog.tsx)
alongside the existing CloseButton and Trigger entries so the main
action-wrapping close component is documented.


## Examples

### Controlled

Example of a controlled alert dialog with custom state management.

<Demo data={controlledDemo} />

### Composing with Menu

Open an alert dialog from a menu item. Since the trigger is outside the `AlertDialog.Root`, use the controlled `open` / `onOpenChange` props.

<Demo data={menuDemo} />
Comment on lines +99 to +103
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Use the exported root name in this note.

The public API is <AlertDialog>, not AlertDialog.Root, so this sentence currently points readers to a symbol that isn't part of the composed export.

📝 Suggested wording
-Open an alert dialog from a menu item. Since the trigger is outside the `AlertDialog.Root`, use the controlled `open` / `onOpenChange` props.
+Open an alert dialog from a menu item. Since the trigger is outside `AlertDialog`, use the controlled `open` / `onOpenChange` props.
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
### Composing with Menu
Open an alert dialog from a menu item. Since the trigger is outside the `AlertDialog.Root`, use the controlled `open` / `onOpenChange` props.
<Demo data={menuDemo} />
### Composing with Menu
Open an alert dialog from a menu item. Since the trigger is outside `AlertDialog`, use the controlled `open` / `onOpenChange` props.
<Demo data={menuDemo} />
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/www/src/content/docs/components/alert-dialog/index.mdx` around lines 99
- 103, Update the prose to reference the exported root symbol <AlertDialog>
instead of AlertDialog.Root: change the sentence to say that when the trigger is
outside <AlertDialog> you should use the controlled open / onOpenChange props;
ensure the example line remains (Demo data={menuDemo}) and that the docs mention
the public API symbol AlertDialog (not AlertDialog.Root) so readers are guided
to the correct exported component.


### Discard Changes

A common pattern for confirming destructive navigation. Both actions use `AlertDialog.Close` to dismiss the dialog.

<Demo data={discardDemo} />

### Nested Alert Dialogs

You can nest alert dialogs for multi-step confirmation flows. When a nested alert dialog opens, the parent dialog automatically scales down and becomes slightly transparent.

<Demo data={nestedDemo} />

## Accessibility

- Alert dialog has `role="alertdialog"` and `aria-modal="true"`
- Does not close on outside click (backdrop), requiring explicit user action
- Uses `aria-label` or `aria-labelledby` to identify the dialog
- Uses `aria-describedby` to provide additional context
- Focus is trapped within the alert dialog while open
Loading
Loading