@gv-tech/design-system 2.1.0 → 2.2.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.
- package/.github/CONTRIBUTING.md +14 -10
- package/.github/RELEASING.md +1 -1
- package/.github/copilot-instructions.md +30 -62
- package/.release-please-manifest.json +1 -1
- package/CHANGELOG.md +19 -0
- package/README.md +8 -9
- package/dist/components/ui/theme-toggle.test.d.ts +2 -0
- package/dist/components/ui/theme-toggle.test.d.ts.map +1 -0
- package/dist/design-system.css +1 -1
- package/dist/favicon.png +0 -0
- package/dist/hooks/use-theme.d.ts +52 -0
- package/dist/hooks/use-theme.d.ts.map +1 -0
- package/dist/hooks/use-theme.test.d.ts +2 -0
- package/dist/hooks/use-theme.test.d.ts.map +1 -0
- package/dist/index.cjs.js +2 -2
- package/dist/index.cjs.js.map +1 -1
- package/dist/index.d.ts +2 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.es.js +829 -773
- package/dist/index.es.js.map +1 -1
- package/dist/logo192.png +0 -0
- package/dist/logo512.png +0 -0
- package/dist/manifest.json +3 -3
- package/dist/pages/ColorTokensDocs.d.ts.map +1 -1
- package/dist/pages/components/ThemeToggleDocs.d.ts.map +1 -1
- package/dist/registry/alert-dialog.test.json +1 -1
- package/dist/registry/index.json +7 -0
- package/dist/registry/theme-toggle.json +1 -1
- package/dist/registry/theme-toggle.test.json +13 -0
- package/dist/theme/tokens.d.ts +115 -0
- package/dist/theme/tokens.d.ts.map +1 -0
- package/index.html +1 -1
- package/package.json +9 -9
- package/public/favicon.png +0 -0
- package/public/logo192.png +0 -0
- package/public/logo512.png +0 -0
- package/public/manifest.json +3 -3
- package/scripts/validate.js +1 -0
- package/src/components/ui/alert-dialog.test.tsx +2 -0
- package/src/components/ui/theme-toggle.test.tsx +49 -0
- package/src/components/ui/theme-toggle.tsx +1 -1
- package/src/globals.css +2 -1
- package/src/hooks/use-theme.test.tsx +27 -0
- package/src/hooks/use-theme.ts +15 -0
- package/src/index.ts +2 -1
- package/src/pages/ColorTokensDocs.tsx +173 -136
- package/src/pages/components/ThemeToggleDocs.tsx +35 -8
- package/src/theme/tokens.ts +68 -0
- package/.nvmrc +0 -1
- package/babel.config.js +0 -3
- package/dist/favicon.ico +0 -0
- package/dist/lib/tokens.d.ts +0 -54
- package/dist/lib/tokens.d.ts.map +0 -1
- package/netlify.toml +0 -6
- package/public/favicon.ico +0 -0
- package/serve.json +0 -4
- package/src/lib/tokens.ts +0 -54
- package/temp.md +0 -292
package/dist/logo192.png
CHANGED
|
Binary file
|
package/dist/logo512.png
CHANGED
|
Binary file
|
package/dist/manifest.json
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ColorTokensDocs.d.ts","sourceRoot":"","sources":["../../src/pages/ColorTokensDocs.tsx"],"names":[],"mappings":"AAKA,wBAAgB,eAAe,
|
|
1
|
+
{"version":3,"file":"ColorTokensDocs.d.ts","sourceRoot":"","sources":["../../src/pages/ColorTokensDocs.tsx"],"names":[],"mappings":"AAKA,wBAAgB,eAAe,4CAoN9B"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ThemeToggleDocs.d.ts","sourceRoot":"","sources":["../../../src/pages/components/ThemeToggleDocs.tsx"],"names":[],"mappings":"AAKA,wBAAgB,eAAe,
|
|
1
|
+
{"version":3,"file":"ThemeToggleDocs.d.ts","sourceRoot":"","sources":["../../../src/pages/components/ThemeToggleDocs.tsx"],"names":[],"mappings":"AAKA,wBAAgB,eAAe,4CA+I9B"}
|
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
"files": [
|
|
7
7
|
{
|
|
8
8
|
"path": "ui/alert-dialog.test.tsx",
|
|
9
|
-
"content": "import { render, screen, waitFor } from '@testing-library/react';\nimport userEvent from '@testing-library/user-event';\nimport { describe, expect, it } from 'vitest';\nimport {\n AlertDialog,\n AlertDialogAction,\n AlertDialogCancel,\n AlertDialogContent,\n AlertDialogDescription,\n AlertDialogFooter,\n AlertDialogHeader,\n AlertDialogTitle,\n AlertDialogTrigger,\n} from './alert-dialog';\n\ndescribe('AlertDialog', () => {\n it('renders correctly', async () => {\n render(\n <AlertDialog>\n <AlertDialogTrigger>Open</AlertDialogTrigger>\n <AlertDialogContent>\n <AlertDialogHeader>\n <AlertDialogTitle>Are you sure?</AlertDialogTitle>\n <AlertDialogDescription>This action cannot be undone.</AlertDialogDescription>\n </AlertDialogHeader>\n <AlertDialogFooter>\n <AlertDialogCancel>Cancel</AlertDialogCancel>\n <AlertDialogAction>Continue</AlertDialogAction>\n </AlertDialogFooter>\n </AlertDialogContent>\n </AlertDialog>,\n );\n\n expect(screen.getByText('Open')).toBeInTheDocument();\n expect(screen.queryByText('Are you sure?')).not.toBeInTheDocument();\n\n await userEvent.click(screen.getByText('Open'));\n\n await waitFor(() => {\n expect(screen.getByRole('alertdialog')).toBeInTheDocument();\n expect(screen.getByText('Are you sure?')).toBeInTheDocument();\n expect(screen.getByText('This action cannot be undone.')).toBeInTheDocument();\n });\n });\n\n it('closes when cancel is clicked', async () => {\n render(\n <AlertDialog>\n <AlertDialogTrigger>Open</AlertDialogTrigger>\n <AlertDialogContent>\n <AlertDialogHeader>\n <AlertDialogTitle>Are you sure?</AlertDialogTitle>\n </AlertDialogHeader>\n <AlertDialogFooter>\n <AlertDialogCancel>Cancel</AlertDialogCancel>\n </AlertDialogFooter>\n </AlertDialogContent>\n </AlertDialog>,\n );\n\n await userEvent.click(screen.getByText('Open'));\n await waitFor(() => expect(screen.getByRole('alertdialog')).toBeInTheDocument());\n\n await userEvent.click(screen.getByText('Cancel'));\n await waitFor(() => expect(screen.queryByRole('alertdialog')).not.toBeInTheDocument());\n });\n\n it('closes when action is clicked', async () => {\n render(\n <AlertDialog>\n <AlertDialogTrigger>Open</AlertDialogTrigger>\n <AlertDialogContent>\n <AlertDialogHeader>\n <AlertDialogTitle>Are you sure?</AlertDialogTitle>\n </AlertDialogHeader>\n <AlertDialogFooter>\n <AlertDialogAction>Continue</AlertDialogAction>\n </AlertDialogFooter>\n </AlertDialogContent>\n </AlertDialog>,\n );\n\n await userEvent.click(screen.getByText('Open'));\n await waitFor(() => expect(screen.getByRole('alertdialog')).toBeInTheDocument());\n\n await userEvent.click(screen.getByText('Continue'));\n await waitFor(() => expect(screen.queryByRole('alertdialog')).not.toBeInTheDocument());\n });\n});\n",
|
|
9
|
+
"content": "import { render, screen, waitFor } from '@testing-library/react';\nimport userEvent from '@testing-library/user-event';\nimport { describe, expect, it } from 'vitest';\nimport {\n AlertDialog,\n AlertDialogAction,\n AlertDialogCancel,\n AlertDialogContent,\n AlertDialogDescription,\n AlertDialogFooter,\n AlertDialogHeader,\n AlertDialogTitle,\n AlertDialogTrigger,\n} from './alert-dialog';\n\ndescribe('AlertDialog', () => {\n it('renders correctly', async () => {\n render(\n <AlertDialog>\n <AlertDialogTrigger>Open</AlertDialogTrigger>\n <AlertDialogContent>\n <AlertDialogHeader>\n <AlertDialogTitle>Are you sure?</AlertDialogTitle>\n <AlertDialogDescription>This action cannot be undone.</AlertDialogDescription>\n </AlertDialogHeader>\n <AlertDialogFooter>\n <AlertDialogCancel>Cancel</AlertDialogCancel>\n <AlertDialogAction>Continue</AlertDialogAction>\n </AlertDialogFooter>\n </AlertDialogContent>\n </AlertDialog>,\n );\n\n expect(screen.getByText('Open')).toBeInTheDocument();\n expect(screen.queryByText('Are you sure?')).not.toBeInTheDocument();\n\n await userEvent.click(screen.getByText('Open'));\n\n await waitFor(() => {\n expect(screen.getByRole('alertdialog')).toBeInTheDocument();\n expect(screen.getByText('Are you sure?')).toBeInTheDocument();\n expect(screen.getByText('This action cannot be undone.')).toBeInTheDocument();\n });\n });\n\n it('closes when cancel is clicked', async () => {\n render(\n <AlertDialog>\n <AlertDialogTrigger>Open</AlertDialogTrigger>\n <AlertDialogContent>\n <AlertDialogHeader>\n <AlertDialogTitle>Are you sure?</AlertDialogTitle>\n <AlertDialogDescription>This action cannot be undone.</AlertDialogDescription>\n </AlertDialogHeader>\n <AlertDialogFooter>\n <AlertDialogCancel>Cancel</AlertDialogCancel>\n </AlertDialogFooter>\n </AlertDialogContent>\n </AlertDialog>,\n );\n\n await userEvent.click(screen.getByText('Open'));\n await waitFor(() => expect(screen.getByRole('alertdialog')).toBeInTheDocument());\n\n await userEvent.click(screen.getByText('Cancel'));\n await waitFor(() => expect(screen.queryByRole('alertdialog')).not.toBeInTheDocument());\n });\n\n it('closes when action is clicked', async () => {\n render(\n <AlertDialog>\n <AlertDialogTrigger>Open</AlertDialogTrigger>\n <AlertDialogContent>\n <AlertDialogHeader>\n <AlertDialogTitle>Are you sure?</AlertDialogTitle>\n <AlertDialogDescription>This action cannot be undone.</AlertDialogDescription>\n </AlertDialogHeader>\n <AlertDialogFooter>\n <AlertDialogAction>Continue</AlertDialogAction>\n </AlertDialogFooter>\n </AlertDialogContent>\n </AlertDialog>,\n );\n\n await userEvent.click(screen.getByText('Open'));\n await waitFor(() => expect(screen.getByRole('alertdialog')).toBeInTheDocument());\n\n await userEvent.click(screen.getByText('Continue'));\n await waitFor(() => expect(screen.queryByRole('alertdialog')).not.toBeInTheDocument());\n });\n});\n",
|
|
10
10
|
"type": "registry:ui"
|
|
11
11
|
}
|
|
12
12
|
]
|
package/dist/registry/index.json
CHANGED
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
"files": [
|
|
7
7
|
{
|
|
8
8
|
"path": "ui/theme-toggle.tsx",
|
|
9
|
-
"content": "import { Button } from '@/components/ui/button';\nimport {\n DropdownMenu,\n DropdownMenuContent,\n DropdownMenuItem,\n DropdownMenuTrigger,\n} from '@/components/ui/dropdown-menu';\nimport { cn } from '@/lib/utils';\nimport { Moon, Sun, SunMoon } from 'lucide-react';\
|
|
9
|
+
"content": "import { Button } from '@/components/ui/button';\nimport {\n DropdownMenu,\n DropdownMenuContent,\n DropdownMenuItem,\n DropdownMenuTrigger,\n} from '@/components/ui/dropdown-menu';\nimport { useTheme } from '@/hooks/use-theme';\nimport { cn } from '@/lib/utils';\nimport { Moon, Sun, SunMoon } from 'lucide-react';\n\nexport interface ThemeToggleProps {\n /**\n * The mode of the theme toggle. 'binary' allows toggling between light and dark. 'ternary' allows choosing between\n * light, dark, and system.\n *\n * @default 'binary'\n */\n variant?: 'binary' | 'ternary';\n /** Optional callback when the theme changes. */\n onThemeChange?: (theme: string) => void;\n /** Optional current theme value for external control. */\n customTheme?: string;\n /** Optional className for the button. */\n className?: string;\n}\n\nexport function ThemeToggle({ variant = 'binary', onThemeChange, customTheme, className }: ThemeToggleProps) {\n const { theme: nextTheme, setTheme: setNextTheme, resolvedTheme } = useTheme();\n\n // Use customTheme if provided, otherwise fallback to next-themes\n const currentTheme = customTheme ?? nextTheme;\n\n // Determine the effective theme for icon rendering\n const effectiveTheme = customTheme ? customTheme : resolvedTheme;\n const isDark = effectiveTheme === 'dark';\n const isSystem = currentTheme === 'system';\n\n const handleThemeChange = (newTheme: string) => {\n if (onThemeChange) {\n onThemeChange(newTheme);\n } else {\n setNextTheme(newTheme);\n }\n };\n\n const IconToggle = () => (\n <>\n <Sun\n className={cn(\n 'h-[1.2rem] w-[1.2rem] transition-all',\n !isSystem && !isDark ? 'rotate-0 scale-100' : '-rotate-90 scale-0',\n )}\n />\n <Moon\n className={cn(\n 'absolute h-[1.2rem] w-[1.2rem] transition-all',\n !isSystem && isDark ? 'rotate-0 scale-100' : 'rotate-90 scale-0',\n )}\n />\n <SunMoon\n className={cn(\n 'absolute h-[1.2rem] w-[1.2rem] transition-all',\n isSystem ? 'rotate-0 scale-100' : 'rotate-90 scale-0',\n )}\n />\n <span className=\"sr-only\">Toggle theme</span>\n </>\n );\n\n if (variant === 'ternary') {\n return (\n <DropdownMenu>\n <DropdownMenuTrigger asChild>\n <Button variant=\"ghost\" size=\"icon\" className={cn('relative h-9 w-9', className)}>\n <IconToggle />\n </Button>\n </DropdownMenuTrigger>\n <DropdownMenuContent align=\"end\">\n <DropdownMenuItem onClick={() => handleThemeChange('light')}>\n <Sun className=\"mr-2 h-4 w-4\" />\n <span>Light</span>\n </DropdownMenuItem>\n <DropdownMenuItem onClick={() => handleThemeChange('dark')}>\n <Moon className=\"mr-2 h-4 w-4\" />\n <span>Dark</span>\n </DropdownMenuItem>\n <DropdownMenuItem onClick={() => handleThemeChange('system')}>\n <SunMoon className=\"mr-2 h-4 w-4\" />\n <span>System</span>\n </DropdownMenuItem>\n </DropdownMenuContent>\n </DropdownMenu>\n );\n }\n\n return (\n <Button\n variant=\"ghost\"\n size=\"icon\"\n className={cn('relative h-9 w-9', className)}\n onClick={() => handleThemeChange(currentTheme === 'dark' ? 'light' : 'dark')}\n aria-label=\"Toggle theme\"\n >\n <IconToggle />\n </Button>\n );\n}\n",
|
|
10
10
|
"type": "registry:ui"
|
|
11
11
|
}
|
|
12
12
|
]
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "theme-toggle.test",
|
|
3
|
+
"type": "registry:ui",
|
|
4
|
+
"dependencies": [],
|
|
5
|
+
"registryDependencies": [],
|
|
6
|
+
"files": [
|
|
7
|
+
{
|
|
8
|
+
"path": "ui/theme-toggle.test.tsx",
|
|
9
|
+
"content": "import { ThemeToggle } from '@/components/ui/theme-toggle';\nimport { render, screen } from '@testing-library/react';\nimport userEvent from '@testing-library/user-event';\nimport { ThemeProvider } from 'next-themes';\nimport { describe, expect, it, vi } from 'vitest';\n\n// Mock the useTheme hook to control its return values\nvi.mock('@/hooks/use-theme', async () => {\n const actual = await vi.importActual('@/hooks/use-theme');\n return {\n ...actual,\n useTheme: () => ({\n theme: 'light',\n setTheme: vi.fn(),\n resolvedTheme: 'light',\n tokens: {},\n }),\n };\n});\n\ndescribe('ThemeToggle', () => {\n it('renders binary toggle by default', () => {\n render(\n <ThemeProvider>\n <ThemeToggle />\n </ThemeProvider>,\n );\n // Use role button which is accessible\n const button = screen.getByRole('button', { name: /toggle theme/i });\n expect(button).toBeInTheDocument();\n });\n\n it('renders ternary toggle with dropdown', async () => {\n const user = userEvent.setup();\n render(\n <ThemeProvider>\n <ThemeToggle variant=\"ternary\" />\n </ThemeProvider>,\n );\n const button = screen.getByRole('button'); // Dropdown trigger\n expect(button).toBeInTheDocument();\n\n // Open dropdown\n await user.click(button);\n expect(await screen.findByText('Light')).toBeInTheDocument();\n expect(screen.getByText('Dark')).toBeInTheDocument();\n expect(screen.getByText('System')).toBeInTheDocument();\n });\n});\n",
|
|
10
|
+
"type": "registry:ui"
|
|
11
|
+
}
|
|
12
|
+
]
|
|
13
|
+
}
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Design system tokens for Garcia Ventures.
|
|
3
|
+
* This file defines the core palette and semantic tokens for the theme system.
|
|
4
|
+
*/
|
|
5
|
+
export declare const palette: {
|
|
6
|
+
readonly brand: {
|
|
7
|
+
readonly blue: "hsl(225 73% 57%)";
|
|
8
|
+
readonly green: "hsl(151 66% 27%)";
|
|
9
|
+
readonly floralWhite: "hsl(40 100% 97%)";
|
|
10
|
+
};
|
|
11
|
+
};
|
|
12
|
+
export declare const theme: {
|
|
13
|
+
readonly light: {
|
|
14
|
+
readonly background: "hsl(0 0% 96%)";
|
|
15
|
+
readonly foreground: "hsl(222 47% 11%)";
|
|
16
|
+
readonly card: "hsl(0 0% 100%)";
|
|
17
|
+
readonly cardForeground: "hsl(222 47% 11%)";
|
|
18
|
+
readonly popover: "hsl(0 0% 100%)";
|
|
19
|
+
readonly popoverForeground: "hsl(222 47% 11%)";
|
|
20
|
+
readonly primary: "hsl(225 73% 57%)";
|
|
21
|
+
readonly primaryForeground: "hsl(0 0% 100%)";
|
|
22
|
+
readonly secondary: "hsl(93 28% 54%)";
|
|
23
|
+
readonly secondaryForeground: "hsl(0 0% 100%)";
|
|
24
|
+
readonly muted: "hsl(0 0% 92%)";
|
|
25
|
+
readonly mutedForeground: "hsl(215 16% 47%)";
|
|
26
|
+
readonly accent: "hsl(0 0% 88%)";
|
|
27
|
+
readonly accentForeground: "hsl(222 47% 11%)";
|
|
28
|
+
readonly destructive: "hsl(0 84.2% 60.2%)";
|
|
29
|
+
readonly destructiveForeground: "hsl(0 0% 100%)";
|
|
30
|
+
readonly border: "hsl(0 0% 89%)";
|
|
31
|
+
readonly input: "hsl(0 0% 89%)";
|
|
32
|
+
readonly ring: "hsl(222 47% 11%)";
|
|
33
|
+
readonly radius: "0.5rem";
|
|
34
|
+
};
|
|
35
|
+
readonly dark: {
|
|
36
|
+
readonly background: "hsl(0 0% 9%)";
|
|
37
|
+
readonly foreground: "hsl(0 0% 100%)";
|
|
38
|
+
readonly card: "hsl(0 0% 14%)";
|
|
39
|
+
readonly cardForeground: "hsl(0 0% 100%)";
|
|
40
|
+
readonly popover: "hsl(0 0% 11%)";
|
|
41
|
+
readonly popoverForeground: "hsl(0 0% 100%)";
|
|
42
|
+
readonly primary: "hsl(227 96% 71%)";
|
|
43
|
+
readonly primaryForeground: "hsl(0 0% 9%)";
|
|
44
|
+
readonly secondary: "hsl(96 44% 61%)";
|
|
45
|
+
readonly secondaryForeground: "hsl(0 0% 9%)";
|
|
46
|
+
readonly muted: "hsl(0 0% 6%)";
|
|
47
|
+
readonly mutedForeground: "hsl(0 0% 70%)";
|
|
48
|
+
readonly accent: "hsl(0 0% 15%)";
|
|
49
|
+
readonly accentForeground: "hsl(0 0% 100%)";
|
|
50
|
+
readonly destructive: "hsl(0 62.8% 30.6%)";
|
|
51
|
+
readonly destructiveForeground: "hsl(0 0% 100%)";
|
|
52
|
+
readonly border: "hsl(0 0% 18%)";
|
|
53
|
+
readonly input: "hsl(0 0% 18%)";
|
|
54
|
+
readonly ring: "hsl(0 0% 90%)";
|
|
55
|
+
readonly radius: "0.5rem";
|
|
56
|
+
};
|
|
57
|
+
};
|
|
58
|
+
export declare const tokens: {
|
|
59
|
+
readonly palette: {
|
|
60
|
+
readonly brand: {
|
|
61
|
+
readonly blue: "hsl(225 73% 57%)";
|
|
62
|
+
readonly green: "hsl(151 66% 27%)";
|
|
63
|
+
readonly floralWhite: "hsl(40 100% 97%)";
|
|
64
|
+
};
|
|
65
|
+
};
|
|
66
|
+
readonly theme: {
|
|
67
|
+
readonly light: {
|
|
68
|
+
readonly background: "hsl(0 0% 96%)";
|
|
69
|
+
readonly foreground: "hsl(222 47% 11%)";
|
|
70
|
+
readonly card: "hsl(0 0% 100%)";
|
|
71
|
+
readonly cardForeground: "hsl(222 47% 11%)";
|
|
72
|
+
readonly popover: "hsl(0 0% 100%)";
|
|
73
|
+
readonly popoverForeground: "hsl(222 47% 11%)";
|
|
74
|
+
readonly primary: "hsl(225 73% 57%)";
|
|
75
|
+
readonly primaryForeground: "hsl(0 0% 100%)";
|
|
76
|
+
readonly secondary: "hsl(93 28% 54%)";
|
|
77
|
+
readonly secondaryForeground: "hsl(0 0% 100%)";
|
|
78
|
+
readonly muted: "hsl(0 0% 92%)";
|
|
79
|
+
readonly mutedForeground: "hsl(215 16% 47%)";
|
|
80
|
+
readonly accent: "hsl(0 0% 88%)";
|
|
81
|
+
readonly accentForeground: "hsl(222 47% 11%)";
|
|
82
|
+
readonly destructive: "hsl(0 84.2% 60.2%)";
|
|
83
|
+
readonly destructiveForeground: "hsl(0 0% 100%)";
|
|
84
|
+
readonly border: "hsl(0 0% 89%)";
|
|
85
|
+
readonly input: "hsl(0 0% 89%)";
|
|
86
|
+
readonly ring: "hsl(222 47% 11%)";
|
|
87
|
+
readonly radius: "0.5rem";
|
|
88
|
+
};
|
|
89
|
+
readonly dark: {
|
|
90
|
+
readonly background: "hsl(0 0% 9%)";
|
|
91
|
+
readonly foreground: "hsl(0 0% 100%)";
|
|
92
|
+
readonly card: "hsl(0 0% 14%)";
|
|
93
|
+
readonly cardForeground: "hsl(0 0% 100%)";
|
|
94
|
+
readonly popover: "hsl(0 0% 11%)";
|
|
95
|
+
readonly popoverForeground: "hsl(0 0% 100%)";
|
|
96
|
+
readonly primary: "hsl(227 96% 71%)";
|
|
97
|
+
readonly primaryForeground: "hsl(0 0% 9%)";
|
|
98
|
+
readonly secondary: "hsl(96 44% 61%)";
|
|
99
|
+
readonly secondaryForeground: "hsl(0 0% 9%)";
|
|
100
|
+
readonly muted: "hsl(0 0% 6%)";
|
|
101
|
+
readonly mutedForeground: "hsl(0 0% 70%)";
|
|
102
|
+
readonly accent: "hsl(0 0% 15%)";
|
|
103
|
+
readonly accentForeground: "hsl(0 0% 100%)";
|
|
104
|
+
readonly destructive: "hsl(0 62.8% 30.6%)";
|
|
105
|
+
readonly destructiveForeground: "hsl(0 0% 100%)";
|
|
106
|
+
readonly border: "hsl(0 0% 18%)";
|
|
107
|
+
readonly input: "hsl(0 0% 18%)";
|
|
108
|
+
readonly ring: "hsl(0 0% 90%)";
|
|
109
|
+
readonly radius: "0.5rem";
|
|
110
|
+
};
|
|
111
|
+
};
|
|
112
|
+
};
|
|
113
|
+
export type ThemeTokens = typeof theme.light;
|
|
114
|
+
export type PaletteTokens = typeof palette;
|
|
115
|
+
//# sourceMappingURL=tokens.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tokens.d.ts","sourceRoot":"","sources":["../../src/theme/tokens.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,eAAO,MAAM,OAAO;;;;;;CAOV,CAAC;AAEX,eAAO,MAAM,KAAK;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CA6CR,CAAC;AAEX,eAAO,MAAM,MAAM;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAGT,CAAC;AAEX,MAAM,MAAM,WAAW,GAAG,OAAO,KAAK,CAAC,KAAK,CAAC;AAC7C,MAAM,MAAM,aAAa,GAAG,OAAO,OAAO,CAAC"}
|
package/index.html
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
<html lang="en">
|
|
3
3
|
<head>
|
|
4
4
|
<meta charset="UTF-8" />
|
|
5
|
-
<link rel="icon" type="image/
|
|
5
|
+
<link rel="icon" type="image/png" href="/favicon.png" />
|
|
6
6
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
7
7
|
<title>GV Tech Design System Playground</title>
|
|
8
8
|
</head>
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@gv-tech/design-system",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.2.0",
|
|
4
4
|
"description": "Garcia Ventures react design system",
|
|
5
5
|
"repository": "git@github.com:Garcia-Ventures/gvtech-design.git",
|
|
6
6
|
"license": "MIT",
|
|
@@ -9,14 +9,20 @@
|
|
|
9
9
|
".": {
|
|
10
10
|
"types": "./dist/index.d.ts",
|
|
11
11
|
"import": "./dist/index.es.js",
|
|
12
|
-
"require": "./dist/index.cjs.js"
|
|
12
|
+
"require": "./dist/index.cjs.js",
|
|
13
|
+
"default": "./dist/index.es.js"
|
|
14
|
+
},
|
|
15
|
+
"./style.css": {
|
|
16
|
+
"style": "./dist/design-system.css",
|
|
17
|
+
"import": "./dist/design-system.css",
|
|
18
|
+
"default": "./dist/design-system.css"
|
|
13
19
|
},
|
|
14
|
-
"./style.css": "./dist/design-system.css",
|
|
15
20
|
"./package.json": "./package.json"
|
|
16
21
|
},
|
|
17
22
|
"main": "dist/index.cjs.js",
|
|
18
23
|
"module": "dist/index.es.js",
|
|
19
24
|
"types": "dist/index.d.ts",
|
|
25
|
+
"style": "dist/design-system.css",
|
|
20
26
|
"scripts": {
|
|
21
27
|
"build": "yarn build:registry && tsc -p tsconfig.build.json && VITE_LIB=true vite build",
|
|
22
28
|
"build:registry": "tsx scripts/build-registry.ts",
|
|
@@ -45,12 +51,6 @@
|
|
|
45
51
|
"prettier --write"
|
|
46
52
|
]
|
|
47
53
|
},
|
|
48
|
-
"babel": {
|
|
49
|
-
"presets": [
|
|
50
|
-
"@babel/preset-env",
|
|
51
|
-
"@babel/preset-react"
|
|
52
|
-
]
|
|
53
|
-
},
|
|
54
54
|
"browserslist": {
|
|
55
55
|
"development": [
|
|
56
56
|
"last 1 chrome version",
|
|
Binary file
|
package/public/logo192.png
CHANGED
|
Binary file
|
package/public/logo512.png
CHANGED
|
Binary file
|
package/public/manifest.json
CHANGED
package/scripts/validate.js
CHANGED
|
@@ -50,6 +50,7 @@ describe('AlertDialog', () => {
|
|
|
50
50
|
<AlertDialogContent>
|
|
51
51
|
<AlertDialogHeader>
|
|
52
52
|
<AlertDialogTitle>Are you sure?</AlertDialogTitle>
|
|
53
|
+
<AlertDialogDescription>This action cannot be undone.</AlertDialogDescription>
|
|
53
54
|
</AlertDialogHeader>
|
|
54
55
|
<AlertDialogFooter>
|
|
55
56
|
<AlertDialogCancel>Cancel</AlertDialogCancel>
|
|
@@ -72,6 +73,7 @@ describe('AlertDialog', () => {
|
|
|
72
73
|
<AlertDialogContent>
|
|
73
74
|
<AlertDialogHeader>
|
|
74
75
|
<AlertDialogTitle>Are you sure?</AlertDialogTitle>
|
|
76
|
+
<AlertDialogDescription>This action cannot be undone.</AlertDialogDescription>
|
|
75
77
|
</AlertDialogHeader>
|
|
76
78
|
<AlertDialogFooter>
|
|
77
79
|
<AlertDialogAction>Continue</AlertDialogAction>
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import { ThemeToggle } from '@/components/ui/theme-toggle';
|
|
2
|
+
import { render, screen } from '@testing-library/react';
|
|
3
|
+
import userEvent from '@testing-library/user-event';
|
|
4
|
+
import { ThemeProvider } from 'next-themes';
|
|
5
|
+
import { describe, expect, it, vi } from 'vitest';
|
|
6
|
+
|
|
7
|
+
// Mock the useTheme hook to control its return values
|
|
8
|
+
vi.mock('@/hooks/use-theme', async () => {
|
|
9
|
+
const actual = await vi.importActual('@/hooks/use-theme');
|
|
10
|
+
return {
|
|
11
|
+
...actual,
|
|
12
|
+
useTheme: () => ({
|
|
13
|
+
theme: 'light',
|
|
14
|
+
setTheme: vi.fn(),
|
|
15
|
+
resolvedTheme: 'light',
|
|
16
|
+
tokens: {},
|
|
17
|
+
}),
|
|
18
|
+
};
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
describe('ThemeToggle', () => {
|
|
22
|
+
it('renders binary toggle by default', () => {
|
|
23
|
+
render(
|
|
24
|
+
<ThemeProvider>
|
|
25
|
+
<ThemeToggle />
|
|
26
|
+
</ThemeProvider>,
|
|
27
|
+
);
|
|
28
|
+
// Use role button which is accessible
|
|
29
|
+
const button = screen.getByRole('button', { name: /toggle theme/i });
|
|
30
|
+
expect(button).toBeInTheDocument();
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
it('renders ternary toggle with dropdown', async () => {
|
|
34
|
+
const user = userEvent.setup();
|
|
35
|
+
render(
|
|
36
|
+
<ThemeProvider>
|
|
37
|
+
<ThemeToggle variant="ternary" />
|
|
38
|
+
</ThemeProvider>,
|
|
39
|
+
);
|
|
40
|
+
const button = screen.getByRole('button'); // Dropdown trigger
|
|
41
|
+
expect(button).toBeInTheDocument();
|
|
42
|
+
|
|
43
|
+
// Open dropdown
|
|
44
|
+
await user.click(button);
|
|
45
|
+
expect(await screen.findByText('Light')).toBeInTheDocument();
|
|
46
|
+
expect(screen.getByText('Dark')).toBeInTheDocument();
|
|
47
|
+
expect(screen.getByText('System')).toBeInTheDocument();
|
|
48
|
+
});
|
|
49
|
+
});
|
|
@@ -5,9 +5,9 @@ import {
|
|
|
5
5
|
DropdownMenuItem,
|
|
6
6
|
DropdownMenuTrigger,
|
|
7
7
|
} from '@/components/ui/dropdown-menu';
|
|
8
|
+
import { useTheme } from '@/hooks/use-theme';
|
|
8
9
|
import { cn } from '@/lib/utils';
|
|
9
10
|
import { Moon, Sun, SunMoon } from 'lucide-react';
|
|
10
|
-
import { useTheme } from 'next-themes';
|
|
11
11
|
|
|
12
12
|
export interface ThemeToggleProps {
|
|
13
13
|
/**
|
package/src/globals.css
CHANGED
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { useTheme } from '@/hooks/use-theme';
|
|
2
|
+
import { theme } from '@/theme/tokens';
|
|
3
|
+
import { renderHook } from '@testing-library/react';
|
|
4
|
+
import { ThemeProvider } from 'next-themes';
|
|
5
|
+
import { describe, expect, it } from 'vitest';
|
|
6
|
+
|
|
7
|
+
describe('useTheme', () => {
|
|
8
|
+
it('returns default light tokens when no theme is set', () => {
|
|
9
|
+
const { result } = renderHook(() => useTheme(), {
|
|
10
|
+
wrapper: ({ children }) => <ThemeProvider>{children}</ThemeProvider>,
|
|
11
|
+
});
|
|
12
|
+
|
|
13
|
+
expect(result.current.tokens).toEqual(theme.light);
|
|
14
|
+
});
|
|
15
|
+
|
|
16
|
+
it('returns dark tokens when theme is dark', () => {
|
|
17
|
+
const { result } = renderHook(() => useTheme(), {
|
|
18
|
+
wrapper: ({ children }) => (
|
|
19
|
+
<ThemeProvider defaultTheme="dark" enableSystem={false}>
|
|
20
|
+
{children}
|
|
21
|
+
</ThemeProvider>
|
|
22
|
+
),
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
expect(result.current.tokens).toEqual(theme.dark);
|
|
26
|
+
});
|
|
27
|
+
});
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { theme } from '@/theme/tokens';
|
|
2
|
+
import { useTheme as useNextTheme } from 'next-themes';
|
|
3
|
+
|
|
4
|
+
export function useTheme() {
|
|
5
|
+
const context = useNextTheme();
|
|
6
|
+
const { resolvedTheme } = context;
|
|
7
|
+
|
|
8
|
+
// Default to light theme tokens if resolvedTheme is undefined or invalid
|
|
9
|
+
const activeTokens = resolvedTheme === 'dark' ? theme.dark : theme.light;
|
|
10
|
+
|
|
11
|
+
return {
|
|
12
|
+
...context,
|
|
13
|
+
tokens: activeTokens,
|
|
14
|
+
};
|
|
15
|
+
}
|
package/src/index.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import './globals.css';
|
|
2
2
|
|
|
3
|
-
export * from './lib/tokens';
|
|
4
3
|
export * from './lib/utils';
|
|
4
|
+
export * from './theme/tokens';
|
|
5
5
|
|
|
6
6
|
// UI Components
|
|
7
7
|
export * from './components/ui/accordion';
|
|
@@ -53,4 +53,5 @@ export * from './components/ui/toggle-group';
|
|
|
53
53
|
export * from './components/ui/tooltip';
|
|
54
54
|
|
|
55
55
|
// Hooks
|
|
56
|
+
export * from './hooks/use-theme';
|
|
56
57
|
export * from './hooks/use-toast';
|