dst-rg 1.0.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 (249) hide show
  1. package/.gitlab-ci.yml +43 -0
  2. package/.storybook/main.ts +15 -0
  3. package/.storybook/preview.ts +15 -0
  4. package/README.md +254 -0
  5. package/components.json +21 -0
  6. package/dist/Avatar.png +0 -0
  7. package/dist/assets/index-CCq7hmG3.js +186 -0
  8. package/dist/assets/index-Mg-hjQGu.css +1 -0
  9. package/dist/index.html +15 -0
  10. package/dist/test.png +0 -0
  11. package/dist/vite.svg +1 -0
  12. package/eslint.config.js +29 -0
  13. package/index.html +14 -0
  14. package/package.json +102 -0
  15. package/postcss.config.mjs +11 -0
  16. package/rollup.config.mjs +55 -0
  17. package/src/assets/react.svg +1 -0
  18. package/src/assets/style/animation.css +27 -0
  19. package/src/assets/style/box-shadow.css +25 -0
  20. package/src/assets/style/colors.css +402 -0
  21. package/src/assets/style/dark-theme.css +288 -0
  22. package/src/assets/style/font-size.css +14 -0
  23. package/src/assets/style/gradient.css +3 -0
  24. package/src/assets/style/index.css +12 -0
  25. package/src/assets/style/light-theme.css +148 -0
  26. package/src/assets/style/line-height.css +13 -0
  27. package/src/assets/style/max-width.css +5 -0
  28. package/src/assets/style/radius.css +13 -0
  29. package/src/assets/style/utility-colors.css +166 -0
  30. package/src/components/Accordion/_.stories.tsx +75 -0
  31. package/src/components/Accordion/_.test.tsx +77 -0
  32. package/src/components/Accordion/index.tsx +47 -0
  33. package/src/components/Accordion/type.ts +24 -0
  34. package/src/components/Avatar/_.stories.tsx +179 -0
  35. package/src/components/Avatar/_.style.ts +40 -0
  36. package/src/components/Avatar/_.test.tsx +150 -0
  37. package/src/components/Avatar/_.types.ts +66 -0
  38. package/src/components/Avatar/index.tsx +63 -0
  39. package/src/components/Badge/_.stories.tsx +75 -0
  40. package/src/components/Badge/_.style.ts +53 -0
  41. package/src/components/Badge/_.test.tsx +27 -0
  42. package/src/components/Badge/_.types.ts +11 -0
  43. package/src/components/Badge/index.tsx +42 -0
  44. package/src/components/Breadcrumbs/_.stories.tsx +95 -0
  45. package/src/components/Breadcrumbs/_.test.tsx +29 -0
  46. package/src/components/Breadcrumbs/_.type.ts +15 -0
  47. package/src/components/Breadcrumbs/index.tsx +103 -0
  48. package/src/components/Button/_.stories.tsx +85 -0
  49. package/src/components/Button/_.style.ts +56 -0
  50. package/src/components/Button/_.test.tsx +103 -0
  51. package/src/components/Button/_.types.ts +14 -0
  52. package/src/components/Button/index.tsx +70 -0
  53. package/src/components/Checkbox/_.stories.tsx +96 -0
  54. package/src/components/Checkbox/_.style.ts +24 -0
  55. package/src/components/Checkbox/_.test.tsx +85 -0
  56. package/src/components/Checkbox/_.types.ts +23 -0
  57. package/src/components/Checkbox/index.tsx +93 -0
  58. package/src/components/CheckboxGroup/PaymentCard/_.stories.tsx +104 -0
  59. package/src/components/CheckboxGroup/PaymentCard/_.style.ts +28 -0
  60. package/src/components/CheckboxGroup/PaymentCard/_.test.tsx +58 -0
  61. package/src/components/CheckboxGroup/PaymentCard/_.types.ts +28 -0
  62. package/src/components/CheckboxGroup/PaymentCard/index.tsx +71 -0
  63. package/src/components/CheckboxGroup/PlanCard/_.stories.tsx +165 -0
  64. package/src/components/CheckboxGroup/PlanCard/_.style.ts +32 -0
  65. package/src/components/CheckboxGroup/PlanCard/_.test.tsx +54 -0
  66. package/src/components/CheckboxGroup/PlanCard/_.types.ts +35 -0
  67. package/src/components/CheckboxGroup/PlanCard/index.tsx +53 -0
  68. package/src/components/CheckboxGroup/UserCard/_.stories.tsx +89 -0
  69. package/src/components/CheckboxGroup/UserCard/_.style.ts +42 -0
  70. package/src/components/CheckboxGroup/UserCard/_.test.tsx +66 -0
  71. package/src/components/CheckboxGroup/UserCard/_.types.ts +26 -0
  72. package/src/components/CheckboxGroup/UserCard/index.tsx +75 -0
  73. package/src/components/Dropdown/_.stories.tsx +180 -0
  74. package/src/components/Dropdown/_.style.ts +108 -0
  75. package/src/components/Dropdown/_.test.tsx +334 -0
  76. package/src/components/Dropdown/_.types.ts +12 -0
  77. package/src/components/Dropdown/index.tsx +130 -0
  78. package/src/components/FileUpload/_.stories.tsx +74 -0
  79. package/src/components/FileUpload/_.style.ts +0 -0
  80. package/src/components/FileUpload/_.test.tsx +222 -0
  81. package/src/components/FileUpload/_.types.ts +53 -0
  82. package/src/components/FileUpload/index.tsx +44 -0
  83. package/src/components/ImageMagnify/_.stories.tsx +226 -0
  84. package/src/components/ImageMagnify/_.style.ts +109 -0
  85. package/src/components/ImageMagnify/_.types.ts +44 -0
  86. package/src/components/ImageMagnify/index.tsx +204 -0
  87. package/src/components/Input/_.stories.tsx +177 -0
  88. package/src/components/Input/_.style.ts +79 -0
  89. package/src/components/Input/_.test.tsx +146 -0
  90. package/src/components/Input/_.types.ts +66 -0
  91. package/src/components/Input/index.tsx +231 -0
  92. package/src/components/InputTags/_.stories.tsx +51 -0
  93. package/src/components/InputTags/_.style.ts +28 -0
  94. package/src/components/InputTags/_.test.tsx +123 -0
  95. package/src/components/InputTags/_.types.ts +26 -0
  96. package/src/components/InputTags/index.tsx +140 -0
  97. package/src/components/Message/_.stories.tsx +79 -0
  98. package/src/components/Message/_.style.ts +87 -0
  99. package/src/components/Message/_.test.tsx +73 -0
  100. package/src/components/Message/_.types.ts +13 -0
  101. package/src/components/Message/index.tsx +57 -0
  102. package/src/components/Metric/_.stories.tsx +142 -0
  103. package/src/components/Metric/_.style.ts +14 -0
  104. package/src/components/Metric/_.test.tsx +166 -0
  105. package/src/components/Metric/_.types.ts +18 -0
  106. package/src/components/Metric/index.tsx +100 -0
  107. package/src/components/Modal/_.stories.tsx +93 -0
  108. package/src/components/Modal/_.style.ts +31 -0
  109. package/src/components/Modal/_.test.tsx +90 -0
  110. package/src/components/Modal/_.types.ts +14 -0
  111. package/src/components/Modal/index.tsx +82 -0
  112. package/src/components/Pagination/_.stories.tsx +118 -0
  113. package/src/components/Pagination/_.test.tsx +51 -0
  114. package/src/components/Pagination/index.tsx +256 -0
  115. package/src/components/Pagination/type.ts +48 -0
  116. package/src/components/PriceSlider/_.stories.tsx +107 -0
  117. package/src/components/PriceSlider/_.test.tsx +63 -0
  118. package/src/components/PriceSlider/_.type.tsx +19 -0
  119. package/src/components/PriceSlider/index.tsx +86 -0
  120. package/src/components/Progress/_.stories.tsx +93 -0
  121. package/src/components/Progress/_.style.ts +15 -0
  122. package/src/components/Progress/_.test.tsx +34 -0
  123. package/src/components/Progress/_.types.ts +17 -0
  124. package/src/components/Progress/index.tsx +173 -0
  125. package/src/components/Radio/_.stories.tsx +391 -0
  126. package/src/components/Radio/_.style.ts +33 -0
  127. package/src/components/Radio/_.test.tsx +77 -0
  128. package/src/components/Radio/_.types.ts +14 -0
  129. package/src/components/Radio/index.tsx +59 -0
  130. package/src/components/Select/_.stories.tsx +308 -0
  131. package/src/components/Select/_.style.ts +5 -0
  132. package/src/components/Select/_.types.ts +24 -0
  133. package/src/components/Select/index.tsx +172 -0
  134. package/src/components/Switch/_.stories.tsx +61 -0
  135. package/src/components/Switch/_.test.tsx +69 -0
  136. package/src/components/Switch/_.type.ts +12 -0
  137. package/src/components/Switch/index.tsx +70 -0
  138. package/src/components/Tabs/_.stories.tsx +508 -0
  139. package/src/components/Tabs/_.style.ts +63 -0
  140. package/src/components/Tabs/_.test.tsx +174 -0
  141. package/src/components/Tabs/_.type.ts +19 -0
  142. package/src/components/Tabs/index.tsx +35 -0
  143. package/src/components/Tag/_.stories.tsx +78 -0
  144. package/src/components/Tag/_.style.ts +71 -0
  145. package/src/components/Tag/_.test.tsx +44 -0
  146. package/src/components/Tag/_.types.ts +27 -0
  147. package/src/components/Tag/index.tsx +46 -0
  148. package/src/components/TextArea/_.stories.tsx +62 -0
  149. package/src/components/TextArea/_.style.ts +11 -0
  150. package/src/components/TextArea/_.test.tsx +43 -0
  151. package/src/components/TextArea/_.types.ts +29 -0
  152. package/src/components/TextArea/index.tsx +83 -0
  153. package/src/components/Toast/_.style.tsx +27 -0
  154. package/src/components/Toast/_.type.ts +30 -0
  155. package/src/components/Toast/_.utils.ts +23 -0
  156. package/src/components/Toast/container.tsx +171 -0
  157. package/src/components/Toast/index.tsx +29 -0
  158. package/src/components/Tooltip/_.stories.tsx +106 -0
  159. package/src/components/Tooltip/_.style.ts +27 -0
  160. package/src/components/Tooltip/_.test.tsx +54 -0
  161. package/src/components/Tooltip/_.types.ts +31 -0
  162. package/src/components/Tooltip/index.tsx +80 -0
  163. package/src/components/developers/AmirHossein.tsx +149 -0
  164. package/src/components/developers/Fardin.tsx +130 -0
  165. package/src/components/developers/Maryam.tsx +260 -0
  166. package/src/components/developers/Milad.tsx +431 -0
  167. package/src/components/developers/Rasoul.tsx +198 -0
  168. package/src/components/index.ts +28 -0
  169. package/src/components/ui/accordion.tsx +162 -0
  170. package/src/components/ui/avatars-component/avatar-description.tsx +30 -0
  171. package/src/components/ui/avatars-component/avatar-groups.tsx +68 -0
  172. package/src/components/ui/avatars-component/avatar-single.tsx +50 -0
  173. package/src/components/ui/card.tsx +92 -0
  174. package/src/components/ui/checkbox-group/plan-card/basic/_.test.tsx +66 -0
  175. package/src/components/ui/checkbox-group/plan-card/basic/index.tsx +70 -0
  176. package/src/components/ui/checkbox-group/plan-card/with-header/_.test.tsx +110 -0
  177. package/src/components/ui/checkbox-group/plan-card/with-header/header.test.tsx +96 -0
  178. package/src/components/ui/checkbox-group/plan-card/with-header/header.tsx +74 -0
  179. package/src/components/ui/checkbox-group/plan-card/with-header/index.tsx +65 -0
  180. package/src/components/ui/file-content/File-content.tsx +43 -0
  181. package/src/components/ui/file-uploader-components/file-uploader-box.tsx +76 -0
  182. package/src/components/ui/file-uploader-components/file-uploader-item.tsx +64 -0
  183. package/src/components/ui/icon-wrapper/_.test.tsx +60 -0
  184. package/src/components/ui/icon-wrapper/index.tsx +19 -0
  185. package/src/components/ui/input-component/input-label.tsx +11 -0
  186. package/src/components/ui/number.tsx +18 -0
  187. package/src/components/ui/pagination/card-minimal-center-align.tsx +96 -0
  188. package/src/components/ui/pagination/card-minimal-right-aligne.tsx +90 -0
  189. package/src/components/ui/pagination/default-pagination.tsx +128 -0
  190. package/src/components/ui/pagination/get-pagination-item.tsx +36 -0
  191. package/src/components/ui/pagination/pagination-card-button-group-aligned.tsx +94 -0
  192. package/src/components/ui/pagination/pagination-card-minimal-left-aligned.tsx +90 -0
  193. package/src/components/ui/pagination/pagination-content.tsx +15 -0
  194. package/src/components/ui/pagination/pagination-item.tsx +11 -0
  195. package/src/components/ui/pagination/pagination-link.tsx +42 -0
  196. package/src/components/ui/tab-components/tabs-content.tsx +15 -0
  197. package/src/components/ui/tab-components/tabs-list.tsx +27 -0
  198. package/src/components/ui/tab-components/tabs-trigger.tsx +25 -0
  199. package/src/components/ui/text-content-wrapper.tsx +36 -0
  200. package/src/hooks/useClickOutside.ts +23 -0
  201. package/src/icons/general/ArrowLeft.tsx +31 -0
  202. package/src/icons/general/ArrowRight.tsx +31 -0
  203. package/src/icons/general/activity-heart.tsx +31 -0
  204. package/src/icons/general/activity.tsx +31 -0
  205. package/src/icons/general/anchor.tsx +31 -0
  206. package/src/icons/general/archive.tsx +31 -0
  207. package/src/icons/general/arrow-left.tsx +25 -0
  208. package/src/icons/general/arrow-right.tsx +25 -0
  209. package/src/icons/general/asterisk-01.tsx +31 -0
  210. package/src/icons/general/asterisk-02.tsx +31 -0
  211. package/src/icons/general/at-sign.tsx +31 -0
  212. package/src/icons/general/attention-mark.tsx +43 -0
  213. package/src/icons/general/bookmark-add.tsx +31 -0
  214. package/src/icons/general/bookmark.tsx +31 -0
  215. package/src/icons/general/chevron-left.tsx +25 -0
  216. package/src/icons/general/chevron-right.tsx +25 -0
  217. package/src/icons/general/circle-minues.tsx +25 -0
  218. package/src/icons/general/circle-plus.tsx +25 -0
  219. package/src/icons/general/circle-question-mark.tsx +32 -0
  220. package/src/icons/general/circle.tsx +32 -0
  221. package/src/icons/general/copy.tsx +43 -0
  222. package/src/icons/general/email.tsx +32 -0
  223. package/src/icons/general/home.tsx +25 -0
  224. package/src/icons/general/layer.tsx +36 -0
  225. package/src/icons/general/leading.tsx +19 -0
  226. package/src/icons/general/master-card.tsx +37 -0
  227. package/src/icons/general/minus.tsx +36 -0
  228. package/src/icons/general/plus.tsx +19 -0
  229. package/src/icons/general/remove.tsx +32 -0
  230. package/src/icons/general/slash-divider.tsx +26 -0
  231. package/src/icons/general/tick-box.tsx +37 -0
  232. package/src/icons/general/trailing.tsx +19 -0
  233. package/src/icons/general/unkown-format.tsx +25 -0
  234. package/src/icons/general/visa-card.tsx +38 -0
  235. package/src/icons/general/x-close.tsx +35 -0
  236. package/src/icons/icons.type.ts +7 -0
  237. package/src/index.css +21 -0
  238. package/src/index.ts +3 -0
  239. package/src/lib/utils.ts +6 -0
  240. package/src/lib/zIndexUtils.ts +2 -0
  241. package/src/main.tsx +50 -0
  242. package/src/vite-env.d.ts +1 -0
  243. package/tests/setup.ts +8 -0
  244. package/tsconfig.app.json +31 -0
  245. package/tsconfig.json +7 -0
  246. package/tsconfig.node.json +24 -0
  247. package/tsconfig.rollup.json +12 -0
  248. package/vite.config.ts +20 -0
  249. package/vitest.config.ts +47 -0
@@ -0,0 +1,308 @@
1
+ import type { Meta, StoryObj } from "@storybook/react-vite";
2
+ import { Select } from "./";
3
+ import { selectPropsType } from "./_.types";
4
+
5
+ const meta: Meta<selectPropsType> = {
6
+ title: "Components/Select",
7
+ component: Select,
8
+ tags: ["autodocs"],
9
+ argTypes: {
10
+ disabled: {
11
+ control: { type: "boolean" },
12
+ },
13
+ isLoading: {
14
+ control: { type: "boolean" },
15
+ },
16
+ selectedValueLabel: {
17
+ control: { type: "text" },
18
+ },
19
+ onChange: {
20
+ action: "changed",
21
+ },
22
+ labelStarClass: {
23
+ control: { type: "text" },
24
+ },
25
+ labelClass: {
26
+ control: { type: "text" },
27
+ },
28
+ },
29
+ parameters: {
30
+ layout: "centered",
31
+ },
32
+ args: {
33
+ config: {
34
+ label: "Select an option",
35
+ name: "select",
36
+ isRequiredInput: false,
37
+ canSelectZero: false,
38
+ },
39
+ selectedValueLabel: "Choose an option",
40
+ options: [
41
+ { title: "Option 1", value: 1 },
42
+ { title: "Option 2", value: 2 },
43
+ { title: "Option 3", value: 3 },
44
+ { title: "Option 4", value: 4 },
45
+ { title: "Option 5", value: 5 },
46
+ ],
47
+ onChange: (value) => console.log("Selected:", value),
48
+ className: "min-w-[300px]",
49
+ labelStarClass: "text-red-500",
50
+ labelClass: "text-blue-500",
51
+ },
52
+ };
53
+
54
+ export default meta;
55
+
56
+ type Story = StoryObj<selectPropsType>;
57
+
58
+ export const Default: Story = {};
59
+
60
+ export const WithLabel: Story = {
61
+ args: {
62
+ config: {
63
+ label: "Choose your country",
64
+ name: "country",
65
+ isRequiredInput: false,
66
+ canSelectZero: false,
67
+ },
68
+ selectedValueLabel: "Select country",
69
+ options: [
70
+ { title: "United States", value: 1 },
71
+ { title: "Canada", value: 2 },
72
+ { title: "United Kingdom", value: 3 },
73
+ { title: "Germany", value: 4 },
74
+ { title: "France", value: 5 },
75
+ { title: "Japan", value: 6 },
76
+ { title: "Australia", value: 7 },
77
+ ],
78
+ labelStarClass: "text-red-500",
79
+ labelClass: "text-blue-500",
80
+ },
81
+ };
82
+
83
+ export const Required: Story = {
84
+ args: {
85
+ config: {
86
+ label: "Priority Level",
87
+ name: "priority",
88
+ isRequiredInput: true,
89
+ canSelectZero: false,
90
+ },
91
+ selectedValueLabel: "Select priority",
92
+ options: [
93
+ { title: "Low", value: 1 },
94
+ { title: "Medium", value: 2 },
95
+ { title: "High", value: 3 },
96
+ { title: "Critical", value: 4 },
97
+ ],
98
+ },
99
+ };
100
+
101
+ export const WithHint: Story = {
102
+ args: {
103
+ config: {
104
+ label: "Department",
105
+ name: "department",
106
+ isRequiredInput: false,
107
+ canSelectZero: false,
108
+ hintText: "Select the department you belong to",
109
+ },
110
+ selectedValueLabel: "Choose department",
111
+ options: [
112
+ { title: "Engineering", value: 1 },
113
+ { title: "Design", value: 2 },
114
+ { title: "Marketing", value: 3 },
115
+ { title: "Sales", value: 4 },
116
+ { title: "HR", value: 5 },
117
+ ],
118
+ labelStarClass: "text-red-500",
119
+ labelClass: "text-blue-500",
120
+ },
121
+ };
122
+
123
+ export const WithError: Story = {
124
+ args: {
125
+ config: {
126
+ label: "Payment Method",
127
+ name: "payment",
128
+ isRequiredInput: true,
129
+ canSelectZero: false,
130
+ errorText: "Please select a payment method",
131
+ },
132
+ selectedValueLabel: "Select payment method",
133
+ options: [
134
+ { title: "Credit Card", value: 1 },
135
+ { title: "Debit Card", value: 2 },
136
+ { title: "PayPal", value: 3 },
137
+ { title: "Bank Transfer", value: 4 },
138
+ ],
139
+ labelStarClass: "text-red-500",
140
+ labelClass: "text-blue-500",
141
+ },
142
+ };
143
+
144
+ export const WithTooltip: Story = {
145
+ args: {
146
+ config: {
147
+ label: "Theme Selection",
148
+ name: "theme",
149
+ isRequiredInput: false,
150
+ canSelectZero: false,
151
+ },
152
+ selectedValueLabel: "Choose theme",
153
+ tooltipTitle: "This will change the appearance of your dashboard",
154
+ options: [
155
+ { title: "Light Theme", value: 1 },
156
+ { title: "Dark Theme", value: 2 },
157
+ { title: "Auto Theme", value: 3 },
158
+ ],
159
+ labelStarClass: "text-red-500",
160
+ labelClass: "text-blue-500",
161
+ },
162
+ };
163
+
164
+ export const Disabled: Story = {
165
+ args: {
166
+ config: {
167
+ label: "Disabled Select",
168
+ name: "disabled",
169
+ isRequiredInput: false,
170
+ canSelectZero: false,
171
+ },
172
+ selectedValueLabel: "This is disabled",
173
+ disabled: true,
174
+ options: [
175
+ { title: "Option 1", value: 1 },
176
+ { title: "Option 2", value: 2 },
177
+ ],
178
+ labelStarClass: "text-red-500",
179
+ labelClass: "text-blue-500",
180
+ },
181
+ };
182
+
183
+ export const Loading: Story = {
184
+ args: {
185
+ config: {
186
+ label: "Loading Options",
187
+ name: "loading",
188
+ isRequiredInput: false,
189
+ canSelectZero: false,
190
+ },
191
+ selectedValueLabel: "Loading...",
192
+ isLoading: true,
193
+ options: [
194
+ { title: "Option 1", value: 1 },
195
+ { title: "Option 2", value: 2 },
196
+ ],
197
+ labelStarClass: "text-red-500",
198
+ labelClass: "text-blue-500",
199
+ },
200
+ };
201
+
202
+ export const WithManyOptions: Story = {
203
+ args: {
204
+ config: {
205
+ label: "Select a city",
206
+ name: "city",
207
+ isRequiredInput: false,
208
+ canSelectZero: false,
209
+ },
210
+ selectedValueLabel: "Choose city",
211
+ options: Array.from({ length: 50 }, (_, i) => ({
212
+ title: `City ${i + 1}`,
213
+ value: i + 1,
214
+ })),
215
+ labelStarClass: "text-red-500",
216
+ labelClass: "text-blue-500",
217
+ },
218
+ };
219
+
220
+ export const CanSelectZero: Story = {
221
+ args: {
222
+ config: {
223
+ label: "Quantity",
224
+ name: "quantity",
225
+ isRequiredInput: false,
226
+ canSelectZero: true,
227
+ },
228
+ selectedValueLabel: "Select quantity",
229
+ options: [
230
+ { title: "0", value: 0 },
231
+ { title: "1", value: 1 },
232
+ { title: "2", value: 2 },
233
+ { title: "3", value: 3 },
234
+ { title: "4", value: 4 },
235
+ { title: "5", value: 5 },
236
+ ],
237
+ labelStarClass: "text-red-500",
238
+ labelClass: "text-blue-500",
239
+ },
240
+ };
241
+
242
+ export const CustomStyling: Story = {
243
+ args: {
244
+ config: {
245
+ label: "Custom Styled Select",
246
+ name: "custom",
247
+ isRequiredInput: false,
248
+ canSelectZero: false,
249
+ },
250
+ selectedValueLabel: "Custom appearance",
251
+ buttonClass: "border-blue-500 bg-blue-50 text-blue-700",
252
+ contentClass: "border-blue-200 bg-blue-50",
253
+ className: "min-w-[350px]",
254
+ options: [
255
+ { title: "Custom Option 1", value: 1 },
256
+ { title: "Custom Option 2", value: 2 },
257
+ { title: "Custom Option 3", value: 3 },
258
+ ],
259
+ labelStarClass: "text-red-500",
260
+ labelClass: "text-blue-500",
261
+ },
262
+ };
263
+
264
+ export const FullWidth: Story = {
265
+ args: {
266
+ config: {
267
+ label: "Full Width Select",
268
+ name: "fullwidth",
269
+ isRequiredInput: false,
270
+ canSelectZero: false,
271
+ },
272
+ selectedValueLabel: "Full width select",
273
+ className: "w-full",
274
+ options: [
275
+ { title: "Option 1", value: 1 },
276
+ { title: "Option 2", value: 2 },
277
+ { title: "Option 3", value: 3 },
278
+ ],
279
+ labelStarClass: "text-red-500",
280
+ labelClass: "text-blue-500",
281
+ },
282
+ };
283
+
284
+ export const WithAllFeatures: Story = {
285
+ args: {
286
+ config: {
287
+ label: "Complete Select Example",
288
+ name: "complete",
289
+ isRequiredInput: true,
290
+ canSelectZero: false,
291
+ hintText: "This select has all features enabled",
292
+ },
293
+ selectedValueLabel: "Select an option",
294
+ tooltipTitle: "This is a comprehensive example with all features",
295
+ tooltipClass: "bg-blue-600 text-white",
296
+ buttonClass: "border-green-500 focus:border-green-600",
297
+ contentClass: "border-green-200 bg-green-50",
298
+ className: "min-w-[400px]",
299
+ options: [
300
+ { title: "Feature Complete Option 1", value: 1 },
301
+ { title: "Feature Complete Option 2", value: 2 },
302
+ { title: "Feature Complete Option 3", value: 3 },
303
+ { title: "Feature Complete Option 4", value: 4 },
304
+ ],
305
+ labelStarClass: "text-red-500",
306
+ labelClass: "text-blue-500",
307
+ },
308
+ };
@@ -0,0 +1,5 @@
1
+ export const tooltipBaseClass = "absolute left-1/2 z-10 max-h-[200px] w-72 -translate-x-1/2 -translate-y-11 transform overflow-auto rounded bg-gray-500 p-3 text-start text-xs text-white shadow-md transition-all duration-200"
2
+
3
+ export const buttonBaseClass = "flex items-center justify-between w-full bg-white border border-rborder-primary rounded-md px-2 shadow-xs text-sm text-dark/80 focus:outline-2 focus:outline-rborder-brand disabled:bg-rbg-disabled-subtle disabled:border-rborder-disabled disabled:cursor-not-allowed disabled:bg-gray-100 disabled:text-gray-500 px-4 py-2 text-left"
4
+
5
+ export const contentBaseClass = "absolute left-0 mt-1 w-full rounded-md bg-gray-50 shadow-lg border border-rborder-primary z-50"
@@ -0,0 +1,24 @@
1
+ type configProps = {
2
+ label?: string,
3
+ name: string,
4
+ errorText?: string,
5
+ hintText?: string,
6
+ isRequiredInput?: boolean,
7
+ canSelectZero?: boolean
8
+ }
9
+
10
+ export type selectPropsType = {
11
+ config: configProps;
12
+ isLoading?: boolean;
13
+ disabled?: boolean;
14
+ className?: string;
15
+ tooltipClass?: string;
16
+ tooltipTitle?: string | React.ReactNode;
17
+ selectedValueLabel: string
18
+ onChange: (value: any) => void,
19
+ options: { title: string, value: number }[],
20
+ buttonClass?: string
21
+ contentClass?: string
22
+ labelClass?: string
23
+ labelStarClass?: string
24
+ }
@@ -0,0 +1,172 @@
1
+ import { InputLabel } from "@/components/ui/input-component/input-label";
2
+ import { useClickOutside } from "@/hooks/useClickOutside";
3
+ import { ChevronDown, ChevronUp } from "lucide-react";
4
+ import { useEffect, useRef, useState } from "react";
5
+ import { twMerge } from "tailwind-merge";
6
+ import { buttonBaseClass, contentBaseClass, tooltipBaseClass } from "./_.style";
7
+ import { selectPropsType } from "./_.types";
8
+
9
+ const Select = ({
10
+ config,
11
+ isLoading = false,
12
+ disabled = false,
13
+ className,
14
+ tooltipClass,
15
+ tooltipTitle,
16
+ selectedValueLabel,
17
+ onChange,
18
+ options,
19
+ buttonClass,
20
+ contentClass,
21
+ labelClass,
22
+ labelStarClass
23
+ }: selectPropsType) => {
24
+ const {
25
+ label,
26
+ name,
27
+ errorText,
28
+ hintText,
29
+ isRequiredInput = false,
30
+ canSelectZero = false,
31
+ } = config || {};
32
+
33
+ const [isTooltipVisible, setIsTooltipVisible] = useState(false);
34
+ const [tooltipPosition, setTooltipPosition] = useState(-50);
35
+
36
+ const [isOpen, setIsOpen] = useState(false);
37
+ const [visibleOptions, setVisibleOptions] = useState(5);
38
+
39
+ const dropdownRef = useRef<HTMLDivElement>(null);
40
+
41
+ useClickOutside(dropdownRef, () => setIsOpen(false));
42
+
43
+ const handleScroll = (e: React.UIEvent<HTMLUListElement>) => {
44
+ const { scrollTop, scrollHeight, clientHeight } = e.currentTarget;
45
+ if (scrollTop + clientHeight >= scrollHeight - 10) {
46
+ setVisibleOptions((prev) => Math.min(prev + 5, options?.length));
47
+ }
48
+ };
49
+
50
+ const handleSelect = (value: string) => {
51
+ onChange({ name, value })
52
+ setIsOpen(false);
53
+ };
54
+
55
+ const tooltipRef = useRef<HTMLDivElement>(null);
56
+
57
+ const calculatePosition = () => {
58
+ if (tooltipRef.current) {
59
+ const tooltipHeight = tooltipRef.current.offsetHeight;
60
+ setTooltipPosition(-tooltipHeight + window.scrollY + 40);
61
+ }
62
+ };
63
+
64
+ useEffect(() => {
65
+ if (isTooltipVisible) {
66
+ calculatePosition();
67
+ }
68
+ }, [isTooltipVisible]);
69
+
70
+ useClickOutside(tooltipRef, () => setIsTooltipVisible(false));
71
+
72
+ return (
73
+ <div className={twMerge("relative flex w-full flex-col gap-1", className)}>
74
+ {label && (
75
+ <div className="flex items-center gap-1">
76
+ <InputLabel
77
+ labelClass={labelClass}
78
+ labelStarClass={labelStarClass}
79
+ id={name} label={label} required={isRequiredInput} />
80
+ {tooltipTitle && (
81
+ <button
82
+ type="button"
83
+ onClick={
84
+ tooltipTitle ? () => setIsTooltipVisible((prev) => !prev) : () => { }
85
+ }
86
+ // onMouseLeave={title ? () => setIsTooltipVisible(false) : () => {}}
87
+ className="flex-center h-4 w-4 rounded-full bg-blue-400 text-xs text-white"
88
+ >
89
+ i
90
+ </button>
91
+ )}
92
+ </div>
93
+ )}
94
+ <div className="relative">
95
+ <button
96
+ type="button"
97
+ className={twMerge(buttonBaseClass, buttonClass)}
98
+ onClick={() => setIsOpen((prev) => !prev)}
99
+ disabled={isLoading || disabled}
100
+ >
101
+ {selectedValueLabel}
102
+ {
103
+ isOpen ?
104
+ <ChevronUp className="h-4 w-4 opacity-50" /> :
105
+ <ChevronDown className="h-4 w-4 opacity-50" />
106
+ }
107
+ </button>
108
+
109
+ {isOpen && (
110
+ <div
111
+ ref={dropdownRef}
112
+ className={twMerge(contentBaseClass, contentClass)}
113
+ >
114
+ <ul
115
+ className="max-h-48 overflow-auto p-2 text-sm text-start"
116
+ onScroll={handleScroll}
117
+ style={{ maxHeight: "150px" }} // Adjust the max height as needed
118
+ >
119
+ {!canSelectZero && (
120
+ <li>
121
+ <button
122
+ type="button"
123
+ className="w-full p-2 text-start text-gray-500 cursor-not-allowed"
124
+ onClick={() => setIsOpen(false)}
125
+ >
126
+ -
127
+ </button>
128
+ </li>
129
+ )}
130
+
131
+ {options?.slice(0, visibleOptions)?.map((opt: any) => (
132
+ <li key={opt.value}>
133
+ <button
134
+ type="button"
135
+ className="w-full p-2 text-start cursor-pointer hover:bg-gray-100"
136
+ onClick={() => handleSelect(opt.value)}
137
+ >
138
+ {opt.title}
139
+ </button>
140
+ </li>
141
+ ))}
142
+ </ul>
143
+ </div>
144
+ )}
145
+ </div>
146
+ {tooltipTitle && isTooltipVisible && (
147
+ <div
148
+ ref={tooltipRef}
149
+ className={twMerge(
150
+ tooltipBaseClass,
151
+ tooltipClass
152
+ )}
153
+ style={{
154
+ top: `${tooltipPosition}px`,
155
+ }}
156
+ dangerouslySetInnerHTML={{ __html: tooltipTitle }}
157
+ />
158
+ )}
159
+ {!errorText && hintText && (
160
+ <p className="text-sm text-rtext-tertiary-600">{hintText}</p>
161
+ )}
162
+ {errorText && (
163
+ <p className="text-sm text-rtext-error-primary-600">
164
+ {errorText}
165
+ </p>
166
+ )}
167
+ </div>
168
+ );
169
+ };
170
+
171
+ export { Select };
172
+
@@ -0,0 +1,61 @@
1
+ import type { StoryObj } from "@storybook/react-vite";
2
+ import { Meta } from "@storybook/react-vite";
3
+ import { Switch } from ".";
4
+ import { SwitchProps } from "./_.type";
5
+
6
+ export default {
7
+ title: "Components/Switch",
8
+ component: Switch,
9
+ tags: ["autodocs"],
10
+ argTypes: {
11
+ direction: {
12
+ control: { type: "radio" },
13
+ options: ["ltr", "rtl"],
14
+ },
15
+ label: {
16
+ control: { type: "text" },
17
+ },
18
+ description: {
19
+ control: { type: "text" },
20
+ },
21
+ disabled: {
22
+ control: { type: "boolean" },
23
+ },
24
+ onCheckedChange: {
25
+ action: "checkedChange",
26
+ },
27
+ },
28
+ args: {
29
+ label: "Toggle me",
30
+ description: "This is a switch description",
31
+ direction: "ltr",
32
+ disabled: false,
33
+ },
34
+ } as Meta<typeof Switch>;
35
+
36
+ type Story = StoryObj<SwitchProps>;
37
+
38
+ export const Default: Story = {};
39
+
40
+ export const RTL: Story = {
41
+ args: {
42
+ dir: "rtl",
43
+ label: "روشن/خاموش",
44
+ description: "توضیحات مربوط به کلید",
45
+ },
46
+ };
47
+
48
+ export const WithoutLabel: Story = {
49
+ args: {
50
+ label: "",
51
+ description: "",
52
+ },
53
+ };
54
+
55
+ export const Disabled: Story = {
56
+ args: {
57
+ disabled: true,
58
+ label: "Disabled Switch",
59
+ description: "You can't toggle this",
60
+ },
61
+ };
@@ -0,0 +1,69 @@
1
+ import { describe, it, expect, vi } from "vitest";
2
+ import { render, screen, fireEvent } from "@testing-library/react";
3
+ import { Switch } from ".";
4
+
5
+ describe("Switch component", () => {
6
+ const createOnToggleMock = () => vi.fn();
7
+
8
+ it("renders with label and description", () => {
9
+ const onToggle = createOnToggleMock();
10
+ render(
11
+ <Switch
12
+ label="Test Label"
13
+ description="Test Description"
14
+ onToggle={onToggle}
15
+ />
16
+ );
17
+
18
+ expect(screen.getByText("Test Label")).toBeInTheDocument();
19
+ expect(screen.getByText("Test Description")).toBeInTheDocument();
20
+ });
21
+
22
+ it("renders correctly in RTL mode", () => {
23
+ const onToggle = createOnToggleMock();
24
+ render(
25
+ <div dir="rtl">
26
+ <Switch label="برچسب" description="توضیحات" onToggle={onToggle} />
27
+ </div>
28
+ );
29
+
30
+ expect(screen.getByText("برچسب")).toBeInTheDocument();
31
+ expect(screen.getByText("توضیحات")).toBeInTheDocument();
32
+ });
33
+
34
+ it("calls onToggle when clicked", () => {
35
+ const onToggle = createOnToggleMock();
36
+ render(<Switch onToggle={onToggle} />);
37
+
38
+ const toggle = screen.getByRole("switch");
39
+ fireEvent.click(toggle);
40
+
41
+ expect(onToggle).toHaveBeenCalled();
42
+ });
43
+
44
+ it("can be toggled by clicking label wrapper", () => {
45
+ const onToggle = createOnToggleMock();
46
+ render(<Switch label="Clickable Label" onToggle={onToggle} />);
47
+
48
+ fireEvent.click(screen.getByText("Clickable Label"));
49
+ expect(onToggle).toHaveBeenCalled();
50
+ });
51
+
52
+ it("supports being disabled", () => {
53
+ const onToggle = createOnToggleMock();
54
+ render(<Switch disabled onToggle={onToggle} />);
55
+ const toggle = screen.getByRole("switch");
56
+ expect(toggle).toBeDisabled();
57
+ });
58
+
59
+ it("respects controlled checked state (manual rerender)", () => {
60
+ const onToggle = createOnToggleMock();
61
+ const { rerender } = render(<Switch checked={false} onToggle={onToggle} />);
62
+ const toggle = screen.getByRole("switch");
63
+
64
+ expect(toggle.getAttribute("data-state")).toBe("unchecked");
65
+
66
+ rerender(<Switch checked={true} onToggle={onToggle} />);
67
+ expect(toggle.getAttribute("data-state")).toBe("checked");
68
+ });
69
+ });
@@ -0,0 +1,12 @@
1
+ import * as SwitchPrimitives from "@radix-ui/react-switch";
2
+ import * as React from "react";
3
+
4
+ export interface SwitchProps
5
+ extends React.ComponentPropsWithoutRef<typeof SwitchPrimitives.Root> {
6
+ thumbClassName?: string;
7
+ label?: string;
8
+ labelClassName?: string;
9
+ description?: string;
10
+ descriptionClassName?: string;
11
+ onToggle: () => void;
12
+ }