import { StoryObj, Meta, StoryFn } from "@storybook/react-vite";
import { within, expect, userEvent, fn } from "storybook/test";
import Alert from "./alert";
import WithInstructions from "#decorators/instructions.jsx";
import React from "react";
const ButtonDecorator = (Story: StoryFn) => {
const [isOpen, setIsOpen] = React.useState(false);
return (
);
};
const instructions = (
<>
We are testing the following interactions on the alert:
Tab through the alert.
Check if the button gets focused.
Click the button to dismiss the alert.
Check that the alert is no longer in the document.
>
);
const meta: Meta = {
title: "FP.React Components/Alert",
component: Alert,
tags: ["beta"],
parameters: {
docs: {
description: {
component: `The Alert component is used to display important messages to users with support for multiple severity levels, variants, and accessibility features.
## CSS Variables
Customize the Alert appearance using CSS custom properties:
### Layout & Structure
- \`--alert-bg\`: Background color (default: \`whitesmoke\`)
- \`--alert-color\`: Text color (default: \`currentColor\`)
- \`--alert-border\`: Border style (default: \`thin solid currentColor\`)
- \`--alert-radius\`: Border radius (default: \`var(--border-radius)\`)
- \`--alert-padding\`: Internal padding (default: \`var(--spc-4)\`)
- \`--alert-margin-block-end\`: Bottom margin (default: none)
- \`--alert-gap\`: Gap between icon and content (default: \`var(--spc-4)\`)
### Severity Colors (Success)
- \`--alert-success-bg\`: Success background (default: \`#e6f4ea\`)
- \`--alert-success-border\`: Success border (default: \`#34a853\`)
- \`--alert-success-text\`: Success text (default: \`#1e4620\`)
### Severity Colors (Error)
- \`--alert-error-bg\`: Error background (default: \`#fdeded\`)
- \`--alert-error-border\`: Error border (default: \`#d32f2f\`)
- \`--alert-error-text\`: Error text (default: \`#5f2120\`)
### Severity Colors (Warning)
- \`--alert-warning-bg\`: Warning background (default: \`#fff4e5\`)
- \`--alert-warning-border\`: Warning border (default: \`#ff9800\`)
- \`--alert-warning-text\`: Warning text (default: \`#663c00\`)
### Severity Colors (Info)
- \`--alert-info-bg\`: Info background (default: \`#e5f6fd\`)
- \`--alert-info-border\`: Info border (default: \`#0288d1\`)
- \`--alert-info-text\`: Info text (default: \`#084154\`)
### Typography
- \`--alert-title-fw\`: Title font weight (default: \`600\`)
- \`--alert-title-fs\`: Title font size (default: \`inherit\`)
### Animation
- \`--alert-transition-duration\`: Animation timing (default: \`0.3s\`)
**Example:**
\`\`\`css
[role="alert"] {
--alert-radius: 0.5rem;
--alert-padding: 1.5rem;
--alert-gap: 1rem;
--alert-title-fw: 700;
--alert-success-bg: #d4edda;
--alert-success-border: #28a745;
}
\`\`\``,
},
},
},
args: {
title: "Alert Title",
children:
"The Alert component is used to display important messages to users.",
dismissible: true,
onDismiss: () => fn(),
},
} as Meta;
export default meta;
type Story = StoryObj;
export const DefaultAlert: Story = {
args: {
open: true,
// Override meta.args.children so the assertion below has a stable
// string to match rather than inheriting the long default description.
children: "This is an alert message",
},
play: async ({ canvasElement }) => {
const canvas = within(canvasElement);
expect(canvas.getByText(/this is an alert message/i)).toBeInTheDocument();
},
} as Story;
export const SuccessAlert: Story = {
args: {
open: true,
severity: "success",
},
} as Story;
export const WarningAlert: Story = {
args: {
open: true,
severity: "warning",
},
} as Story;
export const ErrorAlert: Story = {
args: {
open: true,
severity: "error",
},
} as Story;
export const InfoAlert: Story = {
args: {
open: true,
severity: "info",
},
} as Story;
export const OpenAlert: Story = {
decorators: [ButtonDecorator],
} as Story;
export const InteractionTest: Story = {
args: {
open: true,
className: "my alert",
severity: "info",
},
decorators: [WithInstructions(instructions, "Alert interactions test:")],
play: async ({ canvasElement, step }) => {
const canvas = within(canvasElement);
const alert = canvas.getByRole("alert");
const dismissButton = canvas.getByRole("button", { name: /close alert/i });
await step(
"Check that the alert and button are in the document",
async () => {
expect(alert).toBeInTheDocument();
expect(dismissButton).toBeInTheDocument();
}
);
await step(
"Tab through the alert and check if the button gets focused",
async () => {
await userEvent.tab({ delay: 500 });
expect(dismissButton).toHaveFocus();
}
);
await step("Click the button to dismiss the alert", async () => {
await userEvent.click(dismissButton, { delay: 500 });
expect(alert).not.toBeInTheDocument();
});
},
} as Story;
export const AutoDismissAlert: Story = {
args: {
open: true,
severity: "success",
autoHideDuration: 5000,
title: "Auto-Dismiss Alert",
children: "This alert will automatically dismiss after 5 seconds",
},
parameters: {
docs: {
description: {
story: "Alert with automatic dismissal after 5 seconds. Useful for temporary success messages.",
},
},
},
} as Story;
export const WithActions: Story = {
args: {
open: true,
severity: "warning",
title: "Unsaved Changes",
children: "You have unsaved changes. What would you like to do?",
actions: (
<>
>
),
},
parameters: {
docs: {
description: {
story: "Alert with custom action buttons for user interactions.",
},
},
},
} as Story;
export const FilledVariant: Story = {
args: {
open: true,
severity: "error",
variant: "filled",
title: "Error",
children: "An error occurred while processing your request.",
dismissible: true,
},
parameters: {
docs: {
description: {
story: "Alert with filled variant - solid colored background for high emphasis.",
},
},
},
} as Story;
export const SoftVariant: Story = {
args: {
open: true,
severity: "info",
variant: "soft",
title: "Information",
children: "This is an informational message with soft variant styling.",
dismissible: true,
},
parameters: {
docs: {
description: {
story: "Alert with soft variant - subtle background without border.",
},
},
},
} as Story;
export const AutoFocusAlert: Story = {
args: {
open: true,
severity: "error",
autoFocus: true,
title: "Critical Error",
children: "This is a critical alert that automatically receives focus for screen readers.",
dismissible: true,
},
parameters: {
docs: {
description: {
story: "Alert with autoFocus enabled - useful for critical alerts that need immediate attention.",
},
},
},
} as Story;
export const WithHeadingLevel: Story = {
args: {
open: true,
severity: "info",
title: "Section Alert",
titleLevel: 2,
children: "This alert uses an h2 heading for proper document structure. Use titleLevel to maintain heading hierarchy.",
dismissible: true,
},
parameters: {
docs: {
description: {
story: "Alert with configurable heading level (WCAG 1.3.1). Use titleLevel prop (2-6) to maintain proper heading hierarchy in your page structure.",
},
},
},
} as Story;
export const WithPauseOnHover: Story = {
args: {
open: true,
severity: "success",
autoHideDuration: 5000,
pauseOnHover: true,
title: "Hover to Pause",
children: "This alert will pause auto-dismiss when you hover over it or focus on it (WCAG 2.2.1).",
dismissible: true,
},
parameters: {
docs: {
description: {
story: "Alert with pause on hover/focus enabled. Hovering over or focusing the alert pauses the auto-dismiss timer, allowing users more time to read the content.",
},
},
},
} as Story;
export const CustomIconSizes: Story = {
render: () => (
Compact alert with smaller icon, useful for dense layouts or secondary messages.
Standard alert with default icon size, balanced for most use cases.
Prominent alert with larger icon for high-priority messages or larger viewports.
Eye-catching alert with extra-large icon for critical success confirmations.
),
parameters: {
docs: {
description: {
story: "Demonstration of customizable icon sizes. Use the `iconSize` prop to adjust the severity icon size based on your design needs and context.",
},
},
},
} as Story;
export const AccessibilityShowcase: Story = {
render: () => (
Screen readers will announce "Error:" before the message. This alert is automatically focused.
This alert auto-dismisses in 8 seconds but pauses when hovered or focused.
All dismiss buttons meet 44×44px touch target size (WCAG 2.5.5).
),
parameters: {
docs: {
description: {
story: "Showcase of WCAG 2.1 Level AA accessibility features: screen reader announcements, configurable heading levels, pause on interaction, proper focus indicators, and adequate touch targets.",
},
},
},
} as Story;
export const SimpleTextContent: Story = {
args: {
open: true,
severity: "info",
title: "Simple Text Alert",
children: "This is a simple text message that will be wrapped in a paragraph tag automatically.",
dismissible: true,
},
parameters: {
docs: {
description: {
story: "Default content type ('text') automatically wraps simple text content in a paragraph tag for proper semantic HTML.",
},
},
},
} as Story;
export const ComplexContentWithList: Story = {
args: {
open: true,
severity: "warning",
title: "Action Required",
contentType: "node",
children: (
<>
Please complete the following steps to secure your account:
Review your recent login activity
Update your password to a strong, unique passphrase