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
136 changes: 120 additions & 16 deletions src/components/ui/Alert.stories.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,29 @@
import type { Meta, StoryObj } from "@storybook/react";
import { Alert, AlertTitle, AlertDescription } from "./alert";
import React from "react";
import {
AlertTriangle,
CheckCircle,
Info as InfoIcon,
X,
XCircle,
} from "lucide-react";
import { Alert, AlertAction, AlertDescription, AlertTitle } from "./alert";
import { Button } from "./button";

const VARIANT_ICONS = {
default: InfoIcon,
destructive: XCircle,
success: CheckCircle,
warning: AlertTriangle,
info: InfoIcon,
} as const;

type AlertVariant = keyof typeof VARIANT_ICONS;

function AlertIcon({ variant }: { variant: AlertVariant }) {
const Icon = VARIANT_ICONS[variant] ?? InfoIcon;
return <Icon />;
}

const meta = {
title: "UI/Alert",
Expand All @@ -19,50 +43,130 @@ export default meta;
type Story = StoryObj<typeof meta>;

export const Default: Story = {
render: () => (
<Alert variant="default">
args: { variant: "default" },
render: (args) => (
<Alert variant={args.variant}>
<AlertTitle>Title</AlertTitle>
<AlertDescription>Description text goes here.</AlertDescription>
</Alert>
),
};

export const Destructive: Story = {
render: () => (
<Alert variant="destructive">
args: { variant: "destructive" },
render: (args) => (
<Alert variant={args.variant}>
<AlertTitle>Error</AlertTitle>
<AlertDescription>Something went wrong.</AlertDescription>
</Alert>
),
};

export const Success: Story = {
args: {
variant: "default"
},

render: () => (
<Alert variant="success">
args: { variant: "success" },
render: (args) => (
<Alert variant={args.variant}>
<AlertTitle>Success</AlertTitle>
<AlertDescription>Your changes have been saved.</AlertDescription>
</Alert>
)
),
};

export const Warning: Story = {
render: () => (
<Alert variant="warning">
args: { variant: "warning" },
render: (args) => (
<Alert variant={args.variant}>
<AlertTitle>Warning</AlertTitle>
<AlertDescription>Please review before continuing.</AlertDescription>
</Alert>
),
};

export const Info: Story = {
render: () => (
<Alert variant="info">
args: { variant: "info" },
render: (args) => (
<Alert variant={args.variant}>
<AlertTitle>Info</AlertTitle>
<AlertDescription>New update available.</AlertDescription>
</Alert>
),
};

const defaultVariant: AlertVariant = "default";

export const WithDescription: Story = {
args: { variant: defaultVariant },
render: (args) => (
<Alert variant={args.variant}>
<AlertIcon variant={args.variant ?? defaultVariant} />
<AlertTitle>Hold on I need at least a few minutes!</AlertTitle>
<AlertDescription>
Lorem Ipsum is simply dummy text of the printing and typesetting industry.
</AlertDescription>
<AlertAction>
<Button variant="ghost" size="icon-sm" aria-label="Dismiss">
<X />
</Button>
</AlertAction>
</Alert>
),
};

export const WithoutDescription: Story = {
args: { variant: defaultVariant },
render: (args) => (
<Alert variant={args.variant}>
<AlertIcon variant={args.variant ?? defaultVariant} />
<AlertTitle>Hold on I need at least a few minutes!</AlertTitle>
<AlertAction>
<Button variant="ghost" size="icon-sm" aria-label="Dismiss">
<X />
</Button>
</AlertAction>
</Alert>
),
};

export const WithButtons: Story = {
args: { variant: defaultVariant },
render: (args) => (
<Alert variant={args.variant}>
<div className="flex items-center justify-between gap-4 col-start-2">
<AlertIcon variant={args.variant ?? defaultVariant} />
<AlertTitle>Hold on I need at least a few minutes!</AlertTitle>
<div className="flex gap-2">
<Button variant="default">Primary</Button>
<Button variant="outline">Button</Button>
</div>
</div>
<AlertAction>
<Button variant="ghost" size="icon-sm" aria-label="Dismiss">
<X />
</Button>
</AlertAction>
</Alert>
),
};

export const WithDescriptionAndButtons: Story = {
args: { variant: defaultVariant },
render: (args) => (
<Alert variant={args.variant}>
<AlertIcon variant={args.variant ?? defaultVariant} />
<AlertTitle>Hold on I need at least a few minutes!</AlertTitle>
<AlertDescription>
This process may take a few minutes to complete. See the{" "}
<a href="#">documentation</a> for more details.
</AlertDescription>
<div className="flex gap-2 pt-1 col-start-2">
<Button variant="default">Primary</Button>
<Button variant="outline">Button</Button>
</div>
<AlertAction>
<Button variant="ghost" size="icon-sm" aria-label="Dismiss">
<X />
</Button>
</AlertAction>
</Alert>
),
};
128 changes: 128 additions & 0 deletions src/components/ui/Confirmation.stories.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,128 @@
import type { Meta, StoryObj } from "@storybook/react";
import React, { useState } from "react";
import { Confirmation, Button, Notice } from "./index";
import { CircleMinus } from "lucide-react";

function LeaveWithUnsavedChangesDemo() {
const [open, setOpen] = useState(false);
return (
<>
<Button onClick={() => setOpen(true)}>Leave page</Button>
<Confirmation
open={open}
onClose={() => setOpen(false)}
title="Are you sure you want to leave?"
body="You have unsaved changes."
confirmLabel="Leave"
cancelLabel="Stay in page"
onConfirm={() => setOpen(false)}
/>
</>
);
}

function CancelSubscriptionDemo() {
const [open, setOpen] = useState(false);
return (
<>
<Button variant="outline" onClick={() => setOpen(true)}>
Cancel subscription
</Button>
<Confirmation
open={open}
onClose={() => setOpen(false)}
title="Are you sure you want to cancel the subscription plan?"
body={
<Notice variant="info" className="text-sm">
<p className="text-foreground">
Next billing date is <strong>25th March 2024</strong> and reassign
is not possible for recurring subscription
</p>
</Notice>
}
icon={<CircleMinus className="size-5" />}
confirmLabel="Yes, Cancel"
cancelLabel="No"
onConfirm={() => setOpen(false)}
/>
</>
);
}

function DiscardDraftDemo() {
const [open, setOpen] = useState(false);
return (
<>
<Button variant="outline" onClick={() => setOpen(true)}>
Discard draft
</Button>
<Confirmation
open={open}
onClose={() => setOpen(false)}
title="Are you sure you want to discard this draft?"
confirmLabel="Yes, Cancel"
cancelLabel="No"
onConfirm={() => setOpen(false)}
/>
</>
);
}

function DestructiveDemo() {
const [open, setOpen] = useState(false);
return (
<>
<Button variant="destructive" onClick={() => setOpen(true)}>
Delete item
</Button>
<Confirmation
open={open}
onClose={() => setOpen(false)}
title="Delete this item?"
body="This action cannot be undone."
variant="destructive"
confirmLabel="Delete"
cancelLabel="Cancel"
onConfirm={() => setOpen(false)}
/>
</>
);
}

const meta = {
title: "UI/Confirmation",
component: Confirmation,
parameters: { layout: "centered" },
tags: ["autodocs"],
args: {
open: false,
onClose: () => {},
title: "Confirm action",
confirmLabel: "Confirm",
cancelLabel: "Cancel",
},
} satisfies Meta<typeof Confirmation>;

export default meta;

type Story = StoryObj<typeof meta>;

export const LeaveWithUnsavedChanges: Story = {
render: () => <LeaveWithUnsavedChangesDemo />,
args: { title: "Are you sure you want to leave?", body: "You have unsaved changes." },
};

export const CancelSubscription: Story = {
render: () => <CancelSubscriptionDemo />,
args: { title: "Cancel subscription?" },
};

export const DiscardDraft: Story = {
render: () => <DiscardDraftDemo />,
args: { title: "Discard this draft?" },
};

export const Destructive: Story = {
render: () => <DestructiveDemo />,
args: { title: "Delete this item?", variant: "destructive" },
};
Loading
Loading