@fabio.caffarello/react-design-system 1.4.0 → 1.5.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 (36) hide show
  1. package/dist/index.cjs +4 -4
  2. package/dist/index.js +621 -431
  3. package/dist/ui/atoms/NavLink/NavLink.d.ts +5 -5
  4. package/dist/ui/atoms/NavLink/NavLink.stories.d.ts +1 -0
  5. package/dist/ui/atoms/SidebarItem/SidebarItem.d.ts +21 -0
  6. package/dist/ui/atoms/SidebarItem/SidebarItem.stories.d.ts +7 -0
  7. package/dist/ui/atoms/SidebarItem/SidebarItem.test.d.ts +1 -0
  8. package/dist/ui/atoms/index.d.ts +2 -0
  9. package/dist/ui/molecules/NavbarGroup/NavbarGroup.d.ts +23 -0
  10. package/dist/ui/molecules/NavbarGroup/NavbarGroup.stories.d.ts +8 -0
  11. package/dist/ui/molecules/NavbarGroup/NavbarGroup.test.d.ts +1 -0
  12. package/dist/ui/molecules/SidebarGroup/SidebarGroup.d.ts +20 -0
  13. package/dist/ui/molecules/SidebarHeader/SidebarHeader.d.ts +19 -0
  14. package/dist/ui/molecules/index.d.ts +6 -0
  15. package/dist/ui/organisms/Sidebar/Sidebar.d.ts +35 -0
  16. package/dist/ui/organisms/Sidebar/Sidebar.stories.d.ts +8 -0
  17. package/dist/ui/organisms/Sidebar/Sidebar.test.d.ts +1 -0
  18. package/dist/ui/organisms/index.d.ts +2 -0
  19. package/package.json +1 -1
  20. package/src/ui/atoms/NavLink/NavLink.stories.tsx +12 -0
  21. package/src/ui/atoms/NavLink/NavLink.tsx +9 -5
  22. package/src/ui/atoms/SidebarItem/SidebarItem.stories.tsx +55 -0
  23. package/src/ui/atoms/SidebarItem/SidebarItem.test.tsx +25 -0
  24. package/src/ui/atoms/SidebarItem/SidebarItem.tsx +61 -0
  25. package/src/ui/atoms/index.ts +3 -0
  26. package/src/ui/molecules/EmptyState/EmptyState.tsx +2 -0
  27. package/src/ui/molecules/NavbarGroup/NavbarGroup.stories.tsx +62 -0
  28. package/src/ui/molecules/NavbarGroup/NavbarGroup.test.tsx +32 -0
  29. package/src/ui/molecules/NavbarGroup/NavbarGroup.tsx +71 -0
  30. package/src/ui/molecules/SidebarGroup/SidebarGroup.tsx +52 -0
  31. package/src/ui/molecules/SidebarHeader/SidebarHeader.tsx +80 -0
  32. package/src/ui/molecules/index.ts +9 -0
  33. package/src/ui/organisms/Sidebar/Sidebar.stories.tsx +117 -0
  34. package/src/ui/organisms/Sidebar/Sidebar.test.tsx +40 -0
  35. package/src/ui/organisms/Sidebar/Sidebar.tsx +83 -0
  36. package/src/ui/organisms/index.ts +3 -0
@@ -0,0 +1,52 @@
1
+ 'use client';
2
+
3
+ import type { HTMLAttributes, ReactNode } from "react";
4
+ import { Text } from "../../atoms";
5
+
6
+ export interface SidebarGroupProps extends HTMLAttributes<HTMLDivElement> {
7
+ title?: string;
8
+ children: ReactNode;
9
+ }
10
+
11
+ /**
12
+ * SidebarGroup Component
13
+ *
14
+ * A group container for sidebar items with optional title.
15
+ * Follows Atomic Design principles as a Molecule component.
16
+ *
17
+ * @example
18
+ * ```tsx
19
+ * <SidebarGroup title="Agile">
20
+ * <SidebarItem href="/epics">Epics</SidebarItem>
21
+ * <SidebarItem href="/stories">Stories</SidebarItem>
22
+ * </SidebarGroup>
23
+ * ```
24
+ */
25
+ export default function SidebarGroup({
26
+ title,
27
+ children,
28
+ className = "",
29
+ ...props
30
+ }: SidebarGroupProps) {
31
+ const baseClasses = [
32
+ "space-y-1",
33
+ ];
34
+
35
+ const classes = [
36
+ ...baseClasses,
37
+ className,
38
+ ].filter(Boolean).join(" ");
39
+
40
+ return (
41
+ <div className={classes} {...props}>
42
+ {title && (
43
+ <Text as="h3" className="px-4 py-2 text-xs font-semibold text-gray-500 uppercase tracking-wider">
44
+ {title}
45
+ </Text>
46
+ )}
47
+ <div className="space-y-1">
48
+ {children}
49
+ </div>
50
+ </div>
51
+ );
52
+ }
@@ -0,0 +1,80 @@
1
+ 'use client';
2
+
3
+ import type { HTMLAttributes, ReactNode } from "react";
4
+ import { Text } from "../../atoms";
5
+ import { Button } from "../../atoms";
6
+
7
+ export interface SidebarHeaderProps extends HTMLAttributes<HTMLDivElement> {
8
+ title: string;
9
+ onClose?: () => void;
10
+ showCloseButton?: boolean;
11
+ children?: ReactNode;
12
+ }
13
+
14
+ /**
15
+ * SidebarHeader Component
16
+ *
17
+ * Header section of a sidebar with title and optional close button.
18
+ * Follows Atomic Design principles as a Molecule component.
19
+ *
20
+ * @example
21
+ * ```tsx
22
+ * <SidebarHeader title="Navigation" onClose={handleClose} />
23
+ * ```
24
+ */
25
+ export default function SidebarHeader({
26
+ title,
27
+ onClose,
28
+ showCloseButton = false,
29
+ children,
30
+ className = "",
31
+ ...props
32
+ }: SidebarHeaderProps) {
33
+ const baseClasses = [
34
+ "flex",
35
+ "items-center",
36
+ "justify-between",
37
+ "px-4",
38
+ "py-4",
39
+ "border-b",
40
+ "border-gray-200",
41
+ ];
42
+
43
+ const classes = [
44
+ ...baseClasses,
45
+ className,
46
+ ].filter(Boolean).join(" ");
47
+
48
+ return (
49
+ <div className={classes} {...props}>
50
+ <Text as="h2" className="text-lg font-semibold text-gray-900">
51
+ {title}
52
+ </Text>
53
+ <div className="flex items-center space-x-2">
54
+ {children}
55
+ {showCloseButton && onClose && (
56
+ <Button
57
+ variant="secondary"
58
+ onClick={onClose}
59
+ className="p-1"
60
+ aria-label="Close sidebar"
61
+ >
62
+ <svg
63
+ className="h-5 w-5"
64
+ fill="none"
65
+ viewBox="0 0 24 24"
66
+ stroke="currentColor"
67
+ >
68
+ <path
69
+ strokeLinecap="round"
70
+ strokeLinejoin="round"
71
+ strokeWidth={2}
72
+ d="M6 18L18 6M6 6l12 12"
73
+ />
74
+ </svg>
75
+ </Button>
76
+ )}
77
+ </div>
78
+ </div>
79
+ );
80
+ }
@@ -14,3 +14,12 @@ export type { EmptyStateProps } from "./EmptyState/EmptyState";
14
14
 
15
15
  export { default as Dropdown } from "./Dropdown/Dropdown";
16
16
  export type { DropdownProps, DropdownItem } from "./Dropdown/Dropdown";
17
+
18
+ export { default as SidebarGroup } from "./SidebarGroup/SidebarGroup";
19
+ export type { SidebarGroupProps } from "./SidebarGroup/SidebarGroup";
20
+
21
+ export { default as SidebarHeader } from "./SidebarHeader/SidebarHeader";
22
+ export type { SidebarHeaderProps } from "./SidebarHeader/SidebarHeader";
23
+
24
+ export { default as NavbarGroup } from "./NavbarGroup/NavbarGroup";
25
+ export type { NavbarGroupProps } from "./NavbarGroup/NavbarGroup";
@@ -0,0 +1,117 @@
1
+ import type { Meta, StoryObj } from "@storybook/react";
2
+ import Sidebar from "./Sidebar";
3
+
4
+ const meta: Meta<typeof Sidebar> = {
5
+ title: "UI/Organisms/Sidebar",
6
+ component: Sidebar,
7
+ parameters: {
8
+ docs: {
9
+ description: {
10
+ component: "A sidebar navigation component with header, groups, and items. Uses Compound Components pattern.",
11
+ },
12
+ },
13
+ },
14
+ argTypes: {
15
+ variant: {
16
+ control: "select",
17
+ options: ["default", "collapsed"],
18
+ description: "Visual variant of the sidebar",
19
+ },
20
+ title: {
21
+ control: "text",
22
+ description: "Title displayed in the header",
23
+ },
24
+ showHeader: {
25
+ control: "boolean",
26
+ description: "Whether to show the header",
27
+ },
28
+ },
29
+ };
30
+
31
+ export const Default: StoryObj<typeof Sidebar> = {
32
+ args: {
33
+ title: "Navigation",
34
+ showHeader: true,
35
+ children: (
36
+ <>
37
+ <Sidebar.Group title="Agile">
38
+ <Sidebar.Item href="/epics" isActive>
39
+ Epics
40
+ </Sidebar.Item>
41
+ <Sidebar.Item href="/stories">Stories</Sidebar.Item>
42
+ <Sidebar.Item href="/backlog">Backlog</Sidebar.Item>
43
+ <Sidebar.Item href="/kanban">Kanban</Sidebar.Item>
44
+ <Sidebar.Item href="/sprints">Sprints</Sidebar.Item>
45
+ </Sidebar.Group>
46
+ <Sidebar.Group title="Documentation">
47
+ <Sidebar.Item href="/adrs">ADRs</Sidebar.Item>
48
+ <Sidebar.Item href="/roadmap">Roadmap</Sidebar.Item>
49
+ </Sidebar.Group>
50
+ </>
51
+ ),
52
+ },
53
+ };
54
+
55
+ export const WithIcons: StoryObj<typeof Sidebar> = {
56
+ args: {
57
+ title: "Navigation",
58
+ showHeader: true,
59
+ children: (
60
+ <Sidebar.Group title="Agile">
61
+ <Sidebar.Item
62
+ href="/epics"
63
+ isActive
64
+ icon={
65
+ <svg className="h-5 w-5" fill="none" viewBox="0 0 24 24" stroke="currentColor">
66
+ <path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M9 12h6m-6 4h6m2 5H7a2 2 0 01-2-2V5a2 2 0 012-2h5.586a1 1 0 01.707.293l5.414 5.414a1 1 0 01.293.707V19a2 2 0 01-2 2z" />
67
+ </svg>
68
+ }
69
+ >
70
+ Epics
71
+ </Sidebar.Item>
72
+ <Sidebar.Item
73
+ href="/stories"
74
+ icon={
75
+ <svg className="h-5 w-5" fill="none" viewBox="0 0 24 24" stroke="currentColor">
76
+ <path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M9 12h6m-6 4h6m2 5H7a2 2 0 01-2-2V5a2 2 0 012-2h5.586a1 1 0 01.707.293l5.414 5.414a1 1 0 01.293.707V19a2 2 0 01-2 2z" />
77
+ </svg>
78
+ }
79
+ >
80
+ Stories
81
+ </Sidebar.Item>
82
+ </Sidebar.Group>
83
+ ),
84
+ },
85
+ };
86
+
87
+ export const Collapsed: StoryObj<typeof Sidebar> = {
88
+ args: {
89
+ title: "Navigation",
90
+ variant: "collapsed",
91
+ showHeader: true,
92
+ children: (
93
+ <Sidebar.Group>
94
+ <Sidebar.Item href="/epics" isActive>
95
+ Epics
96
+ </Sidebar.Item>
97
+ <Sidebar.Item href="/stories">Stories</Sidebar.Item>
98
+ </Sidebar.Group>
99
+ ),
100
+ },
101
+ };
102
+
103
+ export const WithoutHeader: StoryObj<typeof Sidebar> = {
104
+ args: {
105
+ showHeader: false,
106
+ children: (
107
+ <Sidebar.Group>
108
+ <Sidebar.Item href="/epics" isActive>
109
+ Epics
110
+ </Sidebar.Item>
111
+ <Sidebar.Item href="/stories">Stories</Sidebar.Item>
112
+ </Sidebar.Group>
113
+ ),
114
+ },
115
+ };
116
+
117
+ export default meta;
@@ -0,0 +1,40 @@
1
+ import { describe, it, expect } from "vitest";
2
+ import { render, screen } from "@testing-library/react";
3
+ import Sidebar from "./Sidebar";
4
+
5
+ describe("Sidebar", () => {
6
+ it("renders with title and children", () => {
7
+ render(
8
+ <Sidebar title="Navigation">
9
+ <Sidebar.Group>
10
+ <Sidebar.Item href="/epics">Epics</Sidebar.Item>
11
+ </Sidebar.Group>
12
+ </Sidebar>
13
+ );
14
+ expect(screen.getByText("Navigation")).toBeInTheDocument();
15
+ expect(screen.getByRole("link", { name: /epics/i })).toBeInTheDocument();
16
+ });
17
+
18
+ it("hides header when showHeader is false", () => {
19
+ render(
20
+ <Sidebar title="Navigation" showHeader={false}>
21
+ <Sidebar.Group>
22
+ <Sidebar.Item href="/epics">Epics</Sidebar.Item>
23
+ </Sidebar.Group>
24
+ </Sidebar>
25
+ );
26
+ expect(screen.queryByText("Navigation")).not.toBeInTheDocument();
27
+ });
28
+
29
+ it("applies collapsed variant styles", () => {
30
+ const { container } = render(
31
+ <Sidebar variant="collapsed" title="Navigation">
32
+ <Sidebar.Group>
33
+ <Sidebar.Item href="/epics">Epics</Sidebar.Item>
34
+ </Sidebar.Group>
35
+ </Sidebar>
36
+ );
37
+ const sidebar = container.querySelector("aside");
38
+ expect(sidebar?.className).toContain("w-16");
39
+ });
40
+ });
@@ -0,0 +1,83 @@
1
+ 'use client';
2
+
3
+ import type { HTMLAttributes, ReactNode } from "react";
4
+ import SidebarHeader from "../../molecules/SidebarHeader/SidebarHeader";
5
+ import SidebarGroup from "../../molecules/SidebarGroup/SidebarGroup";
6
+ import SidebarItem from "../../atoms/SidebarItem/SidebarItem";
7
+
8
+ export interface SidebarProps extends HTMLAttributes<HTMLDivElement> {
9
+ variant?: "default" | "collapsed";
10
+ title?: string;
11
+ showHeader?: boolean;
12
+ onClose?: () => void;
13
+ children: ReactNode;
14
+ }
15
+
16
+ /**
17
+ * Sidebar Component
18
+ *
19
+ * A sidebar navigation component with header, groups, and items.
20
+ * Follows Atomic Design principles as an Organism component.
21
+ * Uses Compound Components pattern.
22
+ *
23
+ * @example
24
+ * ```tsx
25
+ * <Sidebar title="Navigation" variant="default">
26
+ * <Sidebar.Group title="Agile">
27
+ * <Sidebar.Item href="/epics" isActive>Epics</Sidebar.Item>
28
+ * <Sidebar.Item href="/stories">Stories</Sidebar.Item>
29
+ * </Sidebar.Group>
30
+ * </Sidebar>
31
+ * ```
32
+ */
33
+ function SidebarComponent({
34
+ variant = "default",
35
+ title,
36
+ showHeader = true,
37
+ onClose,
38
+ children,
39
+ className = "",
40
+ ...props
41
+ }: SidebarProps) {
42
+ const baseClasses = [
43
+ "flex",
44
+ "flex-col",
45
+ "bg-white",
46
+ "border-r",
47
+ "border-gray-200",
48
+ "h-full",
49
+ ];
50
+
51
+ const variantClasses = {
52
+ default: "w-64",
53
+ collapsed: "w-16",
54
+ };
55
+
56
+ const classes = [
57
+ ...baseClasses,
58
+ variantClasses[variant],
59
+ className,
60
+ ].filter(Boolean).join(" ");
61
+
62
+ return (
63
+ <aside className={classes} {...props}>
64
+ {showHeader && title && (
65
+ <SidebarHeader
66
+ title={title}
67
+ onClose={onClose}
68
+ showCloseButton={!!onClose}
69
+ />
70
+ )}
71
+ <nav className="flex-1 overflow-y-auto py-4">
72
+ {children}
73
+ </nav>
74
+ </aside>
75
+ );
76
+ }
77
+
78
+ // Compound Components
79
+ SidebarComponent.Group = SidebarGroup;
80
+ SidebarComponent.Item = SidebarItem;
81
+ SidebarComponent.Header = SidebarHeader;
82
+
83
+ export default SidebarComponent;
@@ -4,3 +4,6 @@ export { default as Modal } from "./Modal/Modal";
4
4
 
5
5
  export { default as Table } from "./Table/Table";
6
6
  export type { TableColumn } from "./Table/Table";
7
+
8
+ export { default as Sidebar } from "./Sidebar/Sidebar";
9
+ export type { SidebarProps } from "./Sidebar/Sidebar";