@yuno-payments/dashboard-design-system 0.0.1
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/.storybook/main.ts +20 -0
- package/.storybook/preview.ts +18 -0
- package/.storybook/vitest.setup.ts +7 -0
- package/README.md +69 -0
- package/components.json +21 -0
- package/eslint.config.js +26 -0
- package/index.html +13 -0
- package/package.json +57 -0
- package/public/vite.svg +1 -0
- package/src/App.css +42 -0
- package/src/App.tsx +11 -0
- package/src/assets/react.svg +1 -0
- package/src/components/atoms/button/button.stories.tsx +222 -0
- package/src/components/atoms/button/button.test.tsx +78 -0
- package/src/components/atoms/button/index.tsx +80 -0
- package/src/components/atoms/checkbox/checkbox.stories.tsx +314 -0
- package/src/components/atoms/checkbox/checkbox.test.tsx +278 -0
- package/src/components/atoms/checkbox/index.tsx +103 -0
- package/src/components/atoms/chip/chip.stories.tsx +317 -0
- package/src/components/atoms/chip/chip.test.tsx +300 -0
- package/src/components/atoms/chip/index.tsx +114 -0
- package/src/components/atoms/input/index.tsx +27 -0
- package/src/components/atoms/link/index.tsx +79 -0
- package/src/components/atoms/link/link.stories.tsx +159 -0
- package/src/components/atoms/link/link.test.tsx +176 -0
- package/src/components/atoms/radiobutton/index.tsx +103 -0
- package/src/components/atoms/radiobutton/radiobutton.stories.tsx +314 -0
- package/src/components/atoms/radiobutton/radiobutton.test.tsx +245 -0
- package/src/components/atoms/tag/index.tsx +196 -0
- package/src/components/atoms/tag/tag.stories.tsx +281 -0
- package/src/components/atoms/tag/tag.test.tsx +282 -0
- package/src/components/atoms/typography/index.tsx +62 -0
- package/src/components/atoms/typography/typography.stories.tsx +214 -0
- package/src/components/atoms/typography/typography.test.tsx +187 -0
- package/src/components/index.tsx +17 -0
- package/src/components/molecules/announcement/announcement.stories.tsx +277 -0
- package/src/components/molecules/announcement/announcement.test.tsx +354 -0
- package/src/components/molecules/announcement/index.tsx +200 -0
- package/src/components/molecules/notification-alert/index.tsx +293 -0
- package/src/components/molecules/notification-alert/notification-alert.stories.tsx +418 -0
- package/src/components/molecules/notification-alert/notification-alert.test.tsx +454 -0
- package/src/components/molecules/popover/index.tsx +175 -0
- package/src/components/molecules/popover/popover.stories.tsx +241 -0
- package/src/components/molecules/popover/popover.test.tsx +191 -0
- package/src/components/molecules/textfield/index.tsx +154 -0
- package/src/components/molecules/textfield/textfield.stories.tsx +168 -0
- package/src/components/molecules/textfield/textfield.test.tsx +157 -0
- package/src/components/molecules/tooltip/index.tsx +263 -0
- package/src/components/molecules/tooltip/tooltip.stories.tsx +363 -0
- package/src/components/molecules/tooltip/tooltip.test.tsx +468 -0
- package/src/components/organisms/dialog/dialog.stories.tsx +522 -0
- package/src/components/organisms/dialog/dialog.test.tsx +525 -0
- package/src/components/organisms/dialog/index.tsx +233 -0
- package/src/components/organisms/dropdown/dropdown.stories.tsx +529 -0
- package/src/components/organisms/dropdown/dropdown.test.tsx +390 -0
- package/src/components/organisms/dropdown/index.tsx +624 -0
- package/src/index.css +184 -0
- package/src/lib/color-utils.ts +94 -0
- package/src/lib/utils.ts +6 -0
- package/src/main.tsx +10 -0
- package/src/stories/Colors.stories.tsx +107 -0
- package/src/stories/Shadows.stories.tsx +110 -0
- package/src/stories/Spacing.stories.tsx +121 -0
- package/src/stories/Typography.stories.tsx +197 -0
- package/src/vite-env.d.ts +1 -0
- package/tsconfig.app.json +33 -0
- package/tsconfig.json +13 -0
- package/tsconfig.node.json +25 -0
- package/vite.config.ts +43 -0
- package/vitest.config.ts +15 -0
- package/vitest.shims.d.ts +1 -0
|
@@ -0,0 +1,233 @@
|
|
|
1
|
+
import * as React from "react"
|
|
2
|
+
import { cva, type VariantProps } from "class-variance-authority"
|
|
3
|
+
import { X, ArrowLeft } from "lucide-react"
|
|
4
|
+
import { cn } from "../../../lib/utils"
|
|
5
|
+
import { Button } from "../../atoms/button"
|
|
6
|
+
|
|
7
|
+
const dialogVariants = cva(
|
|
8
|
+
"fixed inset-0 z-50 flex items-center justify-center p-4",
|
|
9
|
+
{
|
|
10
|
+
variants: {
|
|
11
|
+
verticalAlign: {
|
|
12
|
+
start: "items-start pt-20",
|
|
13
|
+
center: "items-center",
|
|
14
|
+
end: "items-end pb-20",
|
|
15
|
+
},
|
|
16
|
+
},
|
|
17
|
+
defaultVariants: {
|
|
18
|
+
verticalAlign: "center",
|
|
19
|
+
},
|
|
20
|
+
}
|
|
21
|
+
)
|
|
22
|
+
|
|
23
|
+
const dialogContentVariants = cva(
|
|
24
|
+
"relative bg-background rounded-lg shadow-lg border max-h-[90vh] overflow-hidden flex flex-col",
|
|
25
|
+
{
|
|
26
|
+
variants: {
|
|
27
|
+
size: {
|
|
28
|
+
default: "w-full max-w-md",
|
|
29
|
+
sm: "w-full max-w-sm",
|
|
30
|
+
lg: "w-full max-w-lg",
|
|
31
|
+
xl: "w-full max-w-xl",
|
|
32
|
+
fullscreen: "w-full h-full max-w-none max-h-none rounded-none",
|
|
33
|
+
},
|
|
34
|
+
},
|
|
35
|
+
defaultVariants: {
|
|
36
|
+
size: "default",
|
|
37
|
+
},
|
|
38
|
+
}
|
|
39
|
+
)
|
|
40
|
+
|
|
41
|
+
export type VerticalAlign = 'start' | 'center' | 'end'
|
|
42
|
+
export type VariantButtonType = 'primary' | 'secondary' | 'tertiary' | 'text' | 'outlined' | 'contained'
|
|
43
|
+
export type SizeButtonType = 'small' | 'large' | 'medium'
|
|
44
|
+
|
|
45
|
+
export interface IDialogButton {
|
|
46
|
+
key?: string
|
|
47
|
+
variant?: VariantButtonType
|
|
48
|
+
label: string | React.ReactElement
|
|
49
|
+
event: () => void
|
|
50
|
+
size?: SizeButtonType
|
|
51
|
+
loading?: boolean
|
|
52
|
+
disabled?: boolean
|
|
53
|
+
testId?: string
|
|
54
|
+
type?: 'button' | 'reset' | 'submit'
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
export interface DialogProps
|
|
58
|
+
extends Omit<React.HTMLAttributes<HTMLDivElement>, 'title'>,
|
|
59
|
+
VariantProps<typeof dialogVariants> {
|
|
60
|
+
title?: React.ReactElement | string
|
|
61
|
+
open: boolean
|
|
62
|
+
closeIcon: () => void
|
|
63
|
+
buttons?: IDialogButton[]
|
|
64
|
+
widthContainer?: number | string
|
|
65
|
+
heightContainer?: number | string
|
|
66
|
+
backButtonAction?: () => void
|
|
67
|
+
showActions?: boolean
|
|
68
|
+
otherActions?: React.ReactNode
|
|
69
|
+
withHeader?: boolean
|
|
70
|
+
children: React.ReactNode
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
const Dialog = React.forwardRef<HTMLDivElement, DialogProps>(
|
|
74
|
+
(
|
|
75
|
+
{
|
|
76
|
+
className,
|
|
77
|
+
verticalAlign,
|
|
78
|
+
title,
|
|
79
|
+
open,
|
|
80
|
+
closeIcon,
|
|
81
|
+
buttons = [],
|
|
82
|
+
widthContainer,
|
|
83
|
+
heightContainer,
|
|
84
|
+
backButtonAction,
|
|
85
|
+
showActions = true,
|
|
86
|
+
otherActions,
|
|
87
|
+
withHeader = true,
|
|
88
|
+
children,
|
|
89
|
+
...props
|
|
90
|
+
},
|
|
91
|
+
ref
|
|
92
|
+
) => {
|
|
93
|
+
const [isMobile, setIsMobile] = React.useState(false)
|
|
94
|
+
|
|
95
|
+
React.useEffect(() => {
|
|
96
|
+
const checkMobile = () => {
|
|
97
|
+
setIsMobile(window.innerWidth <= 599)
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
checkMobile()
|
|
101
|
+
window.addEventListener('resize', checkMobile)
|
|
102
|
+
return () => window.removeEventListener('resize', checkMobile)
|
|
103
|
+
}, [])
|
|
104
|
+
|
|
105
|
+
React.useEffect(() => {
|
|
106
|
+
if (open) {
|
|
107
|
+
document.body.style.overflow = 'hidden'
|
|
108
|
+
} else {
|
|
109
|
+
document.body.style.overflow = 'unset'
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
return () => {
|
|
113
|
+
document.body.style.overflow = 'unset'
|
|
114
|
+
}
|
|
115
|
+
}, [open])
|
|
116
|
+
|
|
117
|
+
const getButtonVariant = (variant?: VariantButtonType) => {
|
|
118
|
+
switch (variant) {
|
|
119
|
+
case 'primary':
|
|
120
|
+
return 'default'
|
|
121
|
+
case 'secondary':
|
|
122
|
+
return 'outline'
|
|
123
|
+
case 'tertiary':
|
|
124
|
+
return 'ghost'
|
|
125
|
+
default:
|
|
126
|
+
return variant as 'default' | 'outline' | 'ghost' | 'destructive' | 'secondary' | 'link'
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
const getButtonSize = (size?: SizeButtonType) => {
|
|
131
|
+
switch (size) {
|
|
132
|
+
case 'small':
|
|
133
|
+
return 'sm'
|
|
134
|
+
case 'large':
|
|
135
|
+
return 'lg'
|
|
136
|
+
case 'medium':
|
|
137
|
+
default:
|
|
138
|
+
return 'default'
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
if (!open) return null
|
|
143
|
+
|
|
144
|
+
const dialogStyle = {
|
|
145
|
+
width: widthContainer && !isMobile ? widthContainer : undefined,
|
|
146
|
+
height: heightContainer && !isMobile ? heightContainer : undefined,
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
return (
|
|
150
|
+
<>
|
|
151
|
+
<div
|
|
152
|
+
className="fixed inset-0 z-40 bg-black/25"
|
|
153
|
+
onClick={closeIcon}
|
|
154
|
+
aria-hidden="true"
|
|
155
|
+
/>
|
|
156
|
+
|
|
157
|
+
<div className={cn(dialogVariants({ verticalAlign }))}>
|
|
158
|
+
<div
|
|
159
|
+
ref={ref}
|
|
160
|
+
className={cn(
|
|
161
|
+
dialogContentVariants({
|
|
162
|
+
size: isMobile ? "fullscreen" : "default"
|
|
163
|
+
}),
|
|
164
|
+
className
|
|
165
|
+
)}
|
|
166
|
+
style={dialogStyle}
|
|
167
|
+
role="dialog"
|
|
168
|
+
aria-modal="true"
|
|
169
|
+
{...props}
|
|
170
|
+
>
|
|
171
|
+
{withHeader && (
|
|
172
|
+
<div className="flex items-center justify-between p-6 border-b">
|
|
173
|
+
<div className="flex items-center gap-4 flex-1">
|
|
174
|
+
{backButtonAction && (
|
|
175
|
+
<button
|
|
176
|
+
onClick={backButtonAction}
|
|
177
|
+
className="inline-flex items-center justify-center size-6 rounded-sm opacity-70 hover:opacity-100 transition-opacity"
|
|
178
|
+
aria-label="Go back"
|
|
179
|
+
data-testid="back-button"
|
|
180
|
+
>
|
|
181
|
+
<ArrowLeft className="size-4" />
|
|
182
|
+
</button>
|
|
183
|
+
)}
|
|
184
|
+
<div className="text-lg font-semibold leading-none tracking-tight">
|
|
185
|
+
{title}
|
|
186
|
+
</div>
|
|
187
|
+
</div>
|
|
188
|
+
|
|
189
|
+
<div className="flex items-center gap-4">
|
|
190
|
+
{otherActions}
|
|
191
|
+
<button
|
|
192
|
+
onClick={closeIcon}
|
|
193
|
+
className="inline-flex items-center justify-center size-6 rounded-sm opacity-70 hover:opacity-100 transition-opacity"
|
|
194
|
+
aria-label="Close dialog"
|
|
195
|
+
data-testid="close-button"
|
|
196
|
+
>
|
|
197
|
+
<X className="size-4" />
|
|
198
|
+
</button>
|
|
199
|
+
</div>
|
|
200
|
+
</div>
|
|
201
|
+
)}
|
|
202
|
+
|
|
203
|
+
<div className="flex-1 overflow-auto p-6">
|
|
204
|
+
{children}
|
|
205
|
+
</div>
|
|
206
|
+
|
|
207
|
+
{buttons.length > 0 && showActions && (
|
|
208
|
+
<div className="flex items-center justify-end gap-3 p-6 border-t">
|
|
209
|
+
{buttons.map((button, index) => (
|
|
210
|
+
<Button
|
|
211
|
+
key={button.key || index}
|
|
212
|
+
variant={getButtonVariant(button.variant)}
|
|
213
|
+
size={getButtonSize(button.size)}
|
|
214
|
+
onClick={button.event}
|
|
215
|
+
disabled={button.disabled || button.loading}
|
|
216
|
+
data-testid={button.testId}
|
|
217
|
+
type={button.type}
|
|
218
|
+
>
|
|
219
|
+
{button.loading ? "Loading..." : button.label}
|
|
220
|
+
</Button>
|
|
221
|
+
))}
|
|
222
|
+
</div>
|
|
223
|
+
)}
|
|
224
|
+
</div>
|
|
225
|
+
</div>
|
|
226
|
+
</>
|
|
227
|
+
)
|
|
228
|
+
}
|
|
229
|
+
)
|
|
230
|
+
|
|
231
|
+
Dialog.displayName = "Dialog"
|
|
232
|
+
|
|
233
|
+
export { Dialog }
|