@fabio.caffarello/react-design-system 1.3.2 → 1.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (31) hide show
  1. package/dist/index.cjs +4 -4
  2. package/dist/index.js +535 -352
  3. package/dist/ui/atoms/Skeleton/Skeleton.d.ts +20 -0
  4. package/dist/ui/atoms/Skeleton/Skeleton.stories.d.ts +24 -0
  5. package/dist/ui/atoms/Skeleton/Skeleton.test.d.ts +1 -0
  6. package/dist/ui/atoms/Tooltip/Tooltip.d.ts +21 -0
  7. package/dist/ui/atoms/Tooltip/Tooltip.stories.d.ts +23 -0
  8. package/dist/ui/atoms/Tooltip/Tooltip.test.d.ts +1 -0
  9. package/dist/ui/atoms/index.d.ts +4 -0
  10. package/dist/ui/molecules/Dropdown/Dropdown.d.ts +31 -0
  11. package/dist/ui/molecules/Dropdown/Dropdown.stories.d.ts +25 -0
  12. package/dist/ui/molecules/Dropdown/Dropdown.test.d.ts +1 -0
  13. package/dist/ui/molecules/EmptyState/EmptyState.d.ts +26 -0
  14. package/dist/ui/molecules/EmptyState/EmptyState.stories.d.ts +21 -0
  15. package/dist/ui/molecules/EmptyState/EmptyState.test.d.ts +1 -0
  16. package/dist/ui/molecules/index.d.ts +4 -0
  17. package/package.json +1 -1
  18. package/src/ui/atoms/Skeleton/Skeleton.stories.tsx +61 -0
  19. package/src/ui/atoms/Skeleton/Skeleton.test.tsx +40 -0
  20. package/src/ui/atoms/Skeleton/Skeleton.tsx +70 -0
  21. package/src/ui/atoms/Tooltip/Tooltip.stories.tsx +60 -0
  22. package/src/ui/atoms/Tooltip/Tooltip.test.tsx +53 -0
  23. package/src/ui/atoms/Tooltip/Tooltip.tsx +87 -0
  24. package/src/ui/atoms/index.ts +6 -0
  25. package/src/ui/molecules/Dropdown/Dropdown.stories.tsx +58 -0
  26. package/src/ui/molecules/Dropdown/Dropdown.test.tsx +73 -0
  27. package/src/ui/molecules/Dropdown/Dropdown.tsx +125 -0
  28. package/src/ui/molecules/EmptyState/EmptyState.stories.tsx +63 -0
  29. package/src/ui/molecules/EmptyState/EmptyState.test.tsx +55 -0
  30. package/src/ui/molecules/EmptyState/EmptyState.tsx +81 -0
  31. package/src/ui/molecules/index.ts +6 -0
@@ -0,0 +1,20 @@
1
+ import type { HTMLAttributes } from "react";
2
+ export interface SkeletonProps extends HTMLAttributes<HTMLDivElement> {
3
+ variant?: "text" | "card" | "list" | "circle";
4
+ width?: string;
5
+ height?: string;
6
+ lines?: number;
7
+ }
8
+ /**
9
+ * Skeleton Component
10
+ *
11
+ * A skeleton loader component for displaying loading states.
12
+ * Follows Atomic Design principles as an Atom component.
13
+ *
14
+ * @example
15
+ * ```tsx
16
+ * <Skeleton variant="card" />
17
+ * <Skeleton variant="text" lines={3} />
18
+ * ```
19
+ */
20
+ export default function Skeleton({ variant, width, height, lines, className, ...props }: SkeletonProps): import("react/jsx-runtime").JSX.Element;
@@ -0,0 +1,24 @@
1
+ import type { StoryObj } from "@storybook/react";
2
+ import Skeleton from "./Skeleton";
3
+ declare const meta: {
4
+ title: string;
5
+ component: typeof Skeleton;
6
+ parameters: {
7
+ layout: string;
8
+ };
9
+ tags: string[];
10
+ argTypes: {
11
+ variant: {
12
+ control: "select";
13
+ options: string[];
14
+ };
15
+ };
16
+ };
17
+ export default meta;
18
+ type Story = StoryObj<typeof meta>;
19
+ export declare const Text: Story;
20
+ export declare const TextMultipleLines: Story;
21
+ export declare const Card: Story;
22
+ export declare const List: Story;
23
+ export declare const Circle: Story;
24
+ export declare const CustomSize: Story;
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,21 @@
1
+ import type { HTMLAttributes, ReactNode } from "react";
2
+ export interface TooltipProps extends HTMLAttributes<HTMLDivElement> {
3
+ content: string;
4
+ children: ReactNode;
5
+ position?: "top" | "bottom" | "left" | "right";
6
+ delay?: number;
7
+ }
8
+ /**
9
+ * Tooltip Component
10
+ *
11
+ * A tooltip component for displaying additional information on hover.
12
+ * Follows Atomic Design principles as an Atom component.
13
+ *
14
+ * @example
15
+ * ```tsx
16
+ * <Tooltip content="This is a tooltip">
17
+ * <Button>Hover me</Button>
18
+ * </Tooltip>
19
+ * ```
20
+ */
21
+ export default function Tooltip({ content, children, position, delay, className, ...props }: TooltipProps): import("react/jsx-runtime").JSX.Element;
@@ -0,0 +1,23 @@
1
+ import type { StoryObj } from "@storybook/react";
2
+ import Tooltip from "./Tooltip";
3
+ declare const meta: {
4
+ title: string;
5
+ component: typeof Tooltip;
6
+ parameters: {
7
+ layout: string;
8
+ };
9
+ tags: string[];
10
+ argTypes: {
11
+ position: {
12
+ control: "select";
13
+ options: string[];
14
+ };
15
+ };
16
+ };
17
+ export default meta;
18
+ type Story = StoryObj<typeof meta>;
19
+ export declare const Default: Story;
20
+ export declare const Top: Story;
21
+ export declare const Bottom: Story;
22
+ export declare const Left: Story;
23
+ export declare const Right: Story;
@@ -0,0 +1 @@
1
+ export {};
@@ -9,3 +9,7 @@ export { default as Textarea } from "./Textarea/Textarea";
9
9
  export { default as Label } from "./Label/Label";
10
10
  export { default as ErrorMessage } from "./ErrorMessage/ErrorMessage";
11
11
  export { default as NavLink } from "./NavLink/NavLink";
12
+ export { default as Tooltip } from "./Tooltip/Tooltip";
13
+ export type { TooltipProps } from "./Tooltip/Tooltip";
14
+ export { default as Skeleton } from "./Skeleton/Skeleton";
15
+ export type { SkeletonProps } from "./Skeleton/Skeleton";
@@ -0,0 +1,31 @@
1
+ import type { HTMLAttributes, ReactNode } from "react";
2
+ export interface DropdownItem {
3
+ label: string;
4
+ onClick: () => void;
5
+ disabled?: boolean;
6
+ variant?: "default" | "danger";
7
+ }
8
+ export interface DropdownProps extends HTMLAttributes<HTMLDivElement> {
9
+ trigger: ReactNode;
10
+ items: DropdownItem[];
11
+ align?: "left" | "right";
12
+ variant?: "default" | "minimal";
13
+ }
14
+ /**
15
+ * Dropdown Component
16
+ *
17
+ * A dropdown menu component for displaying actions and options.
18
+ * Follows Atomic Design principles as a Molecule component.
19
+ *
20
+ * @example
21
+ * ```tsx
22
+ * <Dropdown
23
+ * trigger={<Button>Actions</Button>}
24
+ * items={[
25
+ * { label: "Edit", onClick: () => handleEdit() },
26
+ * { label: "Delete", onClick: () => handleDelete(), variant: "danger" },
27
+ * ]}
28
+ * />
29
+ * ```
30
+ */
31
+ export default function Dropdown({ trigger, items, align, variant, className, ...props }: DropdownProps): import("react/jsx-runtime").JSX.Element;
@@ -0,0 +1,25 @@
1
+ import type { StoryObj } from "@storybook/react";
2
+ import Dropdown from "./Dropdown";
3
+ declare const meta: {
4
+ title: string;
5
+ component: typeof Dropdown;
6
+ parameters: {
7
+ layout: string;
8
+ };
9
+ tags: string[];
10
+ argTypes: {
11
+ align: {
12
+ control: "select";
13
+ options: string[];
14
+ };
15
+ variant: {
16
+ control: "select";
17
+ options: string[];
18
+ };
19
+ };
20
+ };
21
+ export default meta;
22
+ type Story = StoryObj<typeof meta>;
23
+ export declare const Default: Story;
24
+ export declare const WithDisabledItem: Story;
25
+ export declare const AlignedLeft: Story;
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,26 @@
1
+ import type { HTMLAttributes, ReactNode } from "react";
2
+ export interface EmptyStateProps extends HTMLAttributes<HTMLDivElement> {
3
+ title: string;
4
+ message: string;
5
+ actionLabel?: string;
6
+ onAction?: () => void;
7
+ illustration?: ReactNode;
8
+ variant?: "default" | "withAction" | "withIllustration";
9
+ }
10
+ /**
11
+ * EmptyState Component
12
+ *
13
+ * A component for displaying empty states when there's no content to show.
14
+ * Follows Atomic Design principles as a Molecule component.
15
+ *
16
+ * @example
17
+ * ```tsx
18
+ * <EmptyState
19
+ * title="No epics yet"
20
+ * message="Get started by creating your first epic"
21
+ * actionLabel="Create Epic"
22
+ * onAction={() => router.push('/epics/new')}
23
+ * />
24
+ * ```
25
+ */
26
+ export default function EmptyState({ title, message, actionLabel, onAction, illustration, variant, className, ...props }: EmptyStateProps): import("react/jsx-runtime").JSX.Element;
@@ -0,0 +1,21 @@
1
+ import type { StoryObj } from "@storybook/react";
2
+ import EmptyState from "./EmptyState";
3
+ declare const meta: {
4
+ title: string;
5
+ component: typeof EmptyState;
6
+ parameters: {
7
+ layout: string;
8
+ };
9
+ tags: string[];
10
+ argTypes: {
11
+ variant: {
12
+ control: "select";
13
+ options: string[];
14
+ };
15
+ };
16
+ };
17
+ export default meta;
18
+ type Story = StoryObj<typeof meta>;
19
+ export declare const Default: Story;
20
+ export declare const WithAction: Story;
21
+ export declare const WithIllustration: Story;
@@ -0,0 +1 @@
1
+ export {};
@@ -4,3 +4,7 @@ export { default as Form } from "./Form/Form";
4
4
  export { default as Breadcrumb } from "./Breadcrumb/Breadcrumb";
5
5
  export type { BreadcrumbItem } from "./Breadcrumb/Breadcrumb";
6
6
  export { default as Pagination } from "./Pagination/Pagination";
7
+ export { default as EmptyState } from "./EmptyState/EmptyState";
8
+ export type { EmptyStateProps } from "./EmptyState/EmptyState";
9
+ export { default as Dropdown } from "./Dropdown/Dropdown";
10
+ export type { DropdownProps, DropdownItem } from "./Dropdown/Dropdown";
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@fabio.caffarello/react-design-system",
3
3
  "private": false,
4
- "version": "1.3.2",
4
+ "version": "1.4.0",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
7
7
  "module": "dist/index.js",
@@ -0,0 +1,61 @@
1
+ import type { Meta, StoryObj } from "@storybook/react";
2
+ import Skeleton from "./Skeleton";
3
+
4
+ const meta = {
5
+ title: "Atoms/Skeleton",
6
+ component: Skeleton,
7
+ parameters: {
8
+ layout: "centered",
9
+ },
10
+ tags: ["autodocs"],
11
+ argTypes: {
12
+ variant: {
13
+ control: "select",
14
+ options: ["text", "card", "list", "circle"],
15
+ },
16
+ },
17
+ } satisfies Meta<typeof Skeleton>;
18
+
19
+ export default meta;
20
+ type Story = StoryObj<typeof meta>;
21
+
22
+ export const Text: Story = {
23
+ args: {
24
+ variant: "text",
25
+ },
26
+ };
27
+
28
+ export const TextMultipleLines: Story = {
29
+ args: {
30
+ variant: "text",
31
+ lines: 3,
32
+ },
33
+ };
34
+
35
+ export const Card: Story = {
36
+ args: {
37
+ variant: "card",
38
+ },
39
+ };
40
+
41
+ export const List: Story = {
42
+ args: {
43
+ variant: "list",
44
+ },
45
+ };
46
+
47
+ export const Circle: Story = {
48
+ args: {
49
+ variant: "circle",
50
+ width: "48px",
51
+ height: "48px",
52
+ },
53
+ };
54
+
55
+ export const CustomSize: Story = {
56
+ args: {
57
+ variant: "text",
58
+ width: "200px",
59
+ height: "20px",
60
+ },
61
+ };
@@ -0,0 +1,40 @@
1
+ import { describe, it, expect } from "vitest";
2
+ import { render } from "@testing-library/react";
3
+ import Skeleton from "./Skeleton";
4
+
5
+ describe("Skeleton", () => {
6
+ it("renders with default variant", () => {
7
+ const { container } = render(<Skeleton />);
8
+ const skeleton = container.firstChild as HTMLElement;
9
+ expect(skeleton).toHaveClass("h-4");
10
+ });
11
+
12
+ it("renders with card variant", () => {
13
+ const { container } = render(<Skeleton variant="card" />);
14
+ const skeleton = container.firstChild as HTMLElement;
15
+ expect(skeleton).toHaveClass("h-32");
16
+ });
17
+
18
+ it("renders multiple lines for text variant", () => {
19
+ const { container } = render(<Skeleton variant="text" lines={3} />);
20
+ const lines = container.querySelectorAll(".animate-pulse");
21
+ expect(lines.length).toBe(3);
22
+ });
23
+
24
+ it("applies custom width and height", () => {
25
+ const { container } = render(
26
+ <Skeleton width="100px" height="50px" />
27
+ );
28
+ const skeleton = container.firstChild as HTMLElement;
29
+ expect(skeleton.style.width).toBe("100px");
30
+ expect(skeleton.style.height).toBe("50px");
31
+ });
32
+
33
+ it("applies custom className", () => {
34
+ const { container } = render(
35
+ <Skeleton className="custom-class" />
36
+ );
37
+ const skeleton = container.firstChild as HTMLElement;
38
+ expect(skeleton).toHaveClass("custom-class");
39
+ });
40
+ });
@@ -0,0 +1,70 @@
1
+ import type { HTMLAttributes } from "react";
2
+
3
+ export interface SkeletonProps extends HTMLAttributes<HTMLDivElement> {
4
+ variant?: "text" | "card" | "list" | "circle";
5
+ width?: string;
6
+ height?: string;
7
+ lines?: number;
8
+ }
9
+
10
+ /**
11
+ * Skeleton Component
12
+ *
13
+ * A skeleton loader component for displaying loading states.
14
+ * Follows Atomic Design principles as an Atom component.
15
+ *
16
+ * @example
17
+ * ```tsx
18
+ * <Skeleton variant="card" />
19
+ * <Skeleton variant="text" lines={3} />
20
+ * ```
21
+ */
22
+ export default function Skeleton({
23
+ variant = "text",
24
+ width,
25
+ height,
26
+ lines = 1,
27
+ className = "",
28
+ ...props
29
+ }: SkeletonProps) {
30
+ const baseClasses = [
31
+ "animate-pulse",
32
+ "bg-gray-200",
33
+ "rounded",
34
+ ];
35
+
36
+ const variantClasses: Record<NonNullable<SkeletonProps["variant"]>, string> = {
37
+ text: "h-4",
38
+ card: "h-32",
39
+ list: "h-12",
40
+ circle: "rounded-full",
41
+ };
42
+
43
+ const classes = [
44
+ ...baseClasses,
45
+ variantClasses[variant],
46
+ className,
47
+ ].filter(Boolean).join(" ");
48
+
49
+ const style: React.CSSProperties = {};
50
+ if (width) style.width = width;
51
+ if (height) style.height = height;
52
+
53
+ if (variant === "text" && lines > 1) {
54
+ return (
55
+ <div className="space-y-2" {...props}>
56
+ {Array.from({ length: lines }).map((_, index) => (
57
+ <div
58
+ key={index}
59
+ className={classes}
60
+ style={index === lines - 1 ? { width: "75%" } : style}
61
+ />
62
+ ))}
63
+ </div>
64
+ );
65
+ }
66
+
67
+ return (
68
+ <div className={classes} style={style} {...props} />
69
+ );
70
+ }
@@ -0,0 +1,60 @@
1
+ import type { Meta, StoryObj } from "@storybook/react";
2
+ import Tooltip from "./Tooltip";
3
+ import Button from "../Button/Button";
4
+
5
+ const meta = {
6
+ title: "Atoms/Tooltip",
7
+ component: Tooltip,
8
+ parameters: {
9
+ layout: "centered",
10
+ },
11
+ tags: ["autodocs"],
12
+ argTypes: {
13
+ position: {
14
+ control: "select",
15
+ options: ["top", "bottom", "left", "right"],
16
+ },
17
+ },
18
+ } satisfies Meta<typeof Tooltip>;
19
+
20
+ export default meta;
21
+ type Story = StoryObj<typeof meta>;
22
+
23
+ export const Default: Story = {
24
+ args: {
25
+ content: "This is a tooltip",
26
+ children: <Button>Hover me</Button>,
27
+ },
28
+ };
29
+
30
+ export const Top: Story = {
31
+ args: {
32
+ content: "Tooltip on top",
33
+ children: <Button>Hover me</Button>,
34
+ position: "top",
35
+ },
36
+ };
37
+
38
+ export const Bottom: Story = {
39
+ args: {
40
+ content: "Tooltip on bottom",
41
+ children: <Button>Hover me</Button>,
42
+ position: "bottom",
43
+ },
44
+ };
45
+
46
+ export const Left: Story = {
47
+ args: {
48
+ content: "Tooltip on left",
49
+ children: <Button>Hover me</Button>,
50
+ position: "left",
51
+ },
52
+ };
53
+
54
+ export const Right: Story = {
55
+ args: {
56
+ content: "Tooltip on right",
57
+ children: <Button>Hover me</Button>,
58
+ position: "right",
59
+ },
60
+ };
@@ -0,0 +1,53 @@
1
+ import { describe, it, expect } from "vitest";
2
+ import { render, screen, waitFor } from "@testing-library/react";
3
+ import { fireEvent } from "@testing-library/react";
4
+ import Tooltip from "./Tooltip";
5
+ import Button from "../Button/Button";
6
+
7
+ describe("Tooltip", () => {
8
+ it("renders children", () => {
9
+ render(
10
+ <Tooltip content="Tooltip content">
11
+ <Button>Button</Button>
12
+ </Tooltip>
13
+ );
14
+
15
+ expect(screen.getByText("Button")).toBeInTheDocument();
16
+ });
17
+
18
+ it("shows tooltip on hover", async () => {
19
+ render(
20
+ <Tooltip content="Tooltip content" delay={0}>
21
+ <Button>Button</Button>
22
+ </Tooltip>
23
+ );
24
+
25
+ const button = screen.getByText("Button");
26
+ fireEvent.mouseEnter(button);
27
+
28
+ await waitFor(() => {
29
+ expect(screen.getByText("Tooltip content")).toBeInTheDocument();
30
+ });
31
+ });
32
+
33
+ it("hides tooltip on mouse leave", async () => {
34
+ render(
35
+ <Tooltip content="Tooltip content" delay={0}>
36
+ <Button>Button</Button>
37
+ </Tooltip>
38
+ );
39
+
40
+ const button = screen.getByText("Button");
41
+ fireEvent.mouseEnter(button);
42
+
43
+ await waitFor(() => {
44
+ expect(screen.getByText("Tooltip content")).toBeInTheDocument();
45
+ });
46
+
47
+ fireEvent.mouseLeave(button);
48
+
49
+ await waitFor(() => {
50
+ expect(screen.queryByText("Tooltip content")).not.toBeInTheDocument();
51
+ });
52
+ });
53
+ });
@@ -0,0 +1,87 @@
1
+ 'use client';
2
+
3
+ import type { HTMLAttributes, ReactNode } from "react";
4
+ import { useState } from "react";
5
+
6
+ export interface TooltipProps extends HTMLAttributes<HTMLDivElement> {
7
+ content: string;
8
+ children: ReactNode;
9
+ position?: "top" | "bottom" | "left" | "right";
10
+ delay?: number;
11
+ }
12
+
13
+ /**
14
+ * Tooltip Component
15
+ *
16
+ * A tooltip component for displaying additional information on hover.
17
+ * Follows Atomic Design principles as an Atom component.
18
+ *
19
+ * @example
20
+ * ```tsx
21
+ * <Tooltip content="This is a tooltip">
22
+ * <Button>Hover me</Button>
23
+ * </Tooltip>
24
+ * ```
25
+ */
26
+ export default function Tooltip({
27
+ content,
28
+ children,
29
+ position = "top",
30
+ delay = 200,
31
+ className = "",
32
+ ...props
33
+ }: TooltipProps) {
34
+ const [isVisible, setIsVisible] = useState(false);
35
+ const [timeoutId, setTimeoutId] = useState<number | null>(null);
36
+
37
+ const handleMouseEnter = () => {
38
+ const id = setTimeout(() => {
39
+ setIsVisible(true);
40
+ }, delay);
41
+ setTimeoutId(id);
42
+ };
43
+
44
+ const handleMouseLeave = () => {
45
+ if (timeoutId) {
46
+ clearTimeout(timeoutId);
47
+ setTimeoutId(null);
48
+ }
49
+ setIsVisible(false);
50
+ };
51
+
52
+ const positionClasses: Record<NonNullable<TooltipProps["position"]>, string> = {
53
+ top: "bottom-full left-1/2 transform -translate-x-1/2 mb-2",
54
+ bottom: "top-full left-1/2 transform -translate-x-1/2 mt-2",
55
+ left: "right-full top-1/2 transform -translate-y-1/2 mr-2",
56
+ right: "left-full top-1/2 transform -translate-y-1/2 ml-2",
57
+ };
58
+
59
+ const arrowClasses: Record<NonNullable<TooltipProps["position"]>, string> = {
60
+ top: "top-full left-1/2 transform -translate-x-1/2 border-t-gray-900",
61
+ bottom: "bottom-full left-1/2 transform -translate-x-1/2 border-b-gray-900",
62
+ left: "left-full top-1/2 transform -translate-y-1/2 border-l-gray-900",
63
+ right: "right-full top-1/2 transform -translate-y-1/2 border-r-gray-900",
64
+ };
65
+
66
+ return (
67
+ <div
68
+ className={`relative inline-block ${className}`}
69
+ onMouseEnter={handleMouseEnter}
70
+ onMouseLeave={handleMouseLeave}
71
+ {...props}
72
+ >
73
+ {children}
74
+ {isVisible && (
75
+ <div
76
+ className={`absolute z-50 px-2 py-1 text-xs text-white bg-gray-900 rounded shadow-lg whitespace-nowrap ${positionClasses[position]}`}
77
+ role="tooltip"
78
+ >
79
+ {content}
80
+ <div
81
+ className={`absolute w-0 h-0 border-4 border-transparent ${arrowClasses[position]}`}
82
+ />
83
+ </div>
84
+ )}
85
+ </div>
86
+ );
87
+ }
@@ -19,3 +19,9 @@ export { default as Label } from "./Label/Label";
19
19
  export { default as ErrorMessage } from "./ErrorMessage/ErrorMessage";
20
20
 
21
21
  export { default as NavLink } from "./NavLink/NavLink";
22
+
23
+ export { default as Tooltip } from "./Tooltip/Tooltip";
24
+ export type { TooltipProps } from "./Tooltip/Tooltip";
25
+
26
+ export { default as Skeleton } from "./Skeleton/Skeleton";
27
+ export type { SkeletonProps } from "./Skeleton/Skeleton";
@@ -0,0 +1,58 @@
1
+ import type { Meta, StoryObj } from "@storybook/react";
2
+ import Dropdown from "./Dropdown";
3
+ import { Button } from "../../atoms";
4
+
5
+ const meta = {
6
+ title: "Molecules/Dropdown",
7
+ component: Dropdown,
8
+ parameters: {
9
+ layout: "centered",
10
+ },
11
+ tags: ["autodocs"],
12
+ argTypes: {
13
+ align: {
14
+ control: "select",
15
+ options: ["left", "right"],
16
+ },
17
+ variant: {
18
+ control: "select",
19
+ options: ["default", "minimal"],
20
+ },
21
+ },
22
+ } satisfies Meta<typeof Dropdown>;
23
+
24
+ export default meta;
25
+ type Story = StoryObj<typeof meta>;
26
+
27
+ export const Default: Story = {
28
+ args: {
29
+ trigger: <Button>Actions</Button>,
30
+ items: [
31
+ { label: "Edit", onClick: () => console.log("Edit clicked") },
32
+ { label: "Duplicate", onClick: () => console.log("Duplicate clicked") },
33
+ { label: "Delete", onClick: () => console.log("Delete clicked"), variant: "danger" },
34
+ ],
35
+ },
36
+ };
37
+
38
+ export const WithDisabledItem: Story = {
39
+ args: {
40
+ trigger: <Button>Actions</Button>,
41
+ items: [
42
+ { label: "Edit", onClick: () => console.log("Edit clicked") },
43
+ { label: "Archive", onClick: () => console.log("Archive clicked"), disabled: true },
44
+ { label: "Delete", onClick: () => console.log("Delete clicked"), variant: "danger" },
45
+ ],
46
+ },
47
+ };
48
+
49
+ export const AlignedLeft: Story = {
50
+ args: {
51
+ trigger: <Button>Menu</Button>,
52
+ items: [
53
+ { label: "Option 1", onClick: () => console.log("Option 1") },
54
+ { label: "Option 2", onClick: () => console.log("Option 2") },
55
+ ],
56
+ align: "left",
57
+ },
58
+ };