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,165 @@
1
+ import { useState } from "react";
2
+ import type { Meta, StoryObj } from "@storybook/react-vite";
3
+ import { PlanCard } from ".";
4
+
5
+ const meta: Meta<typeof PlanCard> = {
6
+ title: "Components/PlanCard",
7
+ component: PlanCard,
8
+ tags: ["autodocs"],
9
+ };
10
+ export default meta;
11
+
12
+ type Story = StoryObj<typeof PlanCard>;
13
+
14
+ export const Default: Story = {
15
+ args: {
16
+ title: "Basic Plan",
17
+ price: "$20",
18
+ per: "month",
19
+ size: "md",
20
+ headerTitle: "Subscribe to newsletter",
21
+ description:
22
+ "Get the latest updates, articles, and promotions delivered to your inbox.",
23
+ badgeContent: "limited time only",
24
+ id: "basic-plan",
25
+ checkboxIndex: 0,
26
+ },
27
+ render: (args) => {
28
+ const PlanCardStory = () => {
29
+ const [checked, setChecked] = useState(false);
30
+
31
+ return (
32
+ <PlanCard
33
+ {...args}
34
+ checked={checked}
35
+ onChange={() => setChecked(!checked)}
36
+ />
37
+ );
38
+ };
39
+
40
+ return <PlanCardStory />;
41
+ },
42
+ };
43
+
44
+ export const WithHeader: Story = {
45
+ args: {
46
+ title: "Basic Plan",
47
+ price: "$20",
48
+ per: "month",
49
+ hasHeader: true,
50
+ size: "md",
51
+ headerTitle: "Subscribe to newsletter",
52
+ description:
53
+ "Get the latest updates, articles, and promotions delivered to your inbox.",
54
+ badgeContent: "limited time only",
55
+ id: "basic-plan",
56
+ checkboxIndex: 0,
57
+ },
58
+ render: (args) => {
59
+ const PlanCardStory = () => {
60
+ const [checked, setChecked] = useState(false);
61
+
62
+ return (
63
+ <PlanCard
64
+ {...args}
65
+ checked={checked}
66
+ onChange={() => setChecked(!checked)}
67
+ />
68
+ );
69
+ };
70
+
71
+ return <PlanCardStory />;
72
+ },
73
+ };
74
+
75
+ export const Disabled: Story = {
76
+ args: {
77
+ title: "Basic Plan",
78
+ price: "$20",
79
+ per: "month",
80
+ hasHeader: true,
81
+ size: "md",
82
+ headerTitle: "Subscribe to newsletter",
83
+ description:
84
+ "Get the latest updates, articles, and promotions delivered to your inbox.",
85
+ badgeContent: "limited time only",
86
+ id: "basic-plan",
87
+ checkboxIndex: 0,
88
+ disabled: true,
89
+ },
90
+ render: (args) => {
91
+ const PlanCardStory = () => {
92
+ const [checked, setChecked] = useState(false);
93
+
94
+ return <PlanCard {...args} onChange={() => setChecked(!checked)} />;
95
+ };
96
+
97
+ return <PlanCardStory />;
98
+ },
99
+ };
100
+ export const CheckedDisabled: Story = {
101
+ args: {
102
+ title: "Basic Plan",
103
+ price: "$20",
104
+ per: "month",
105
+ hasHeader: true,
106
+ size: "md",
107
+ headerTitle: "Subscribe to newsletter",
108
+ description:
109
+ "Get the latest updates, articles, and promotions delivered to your inbox.",
110
+ badgeContent: "limited time only",
111
+ id: "basic-plan",
112
+ checkboxIndex: 0,
113
+ disabled: true,
114
+ checked: true,
115
+ },
116
+ render: (args) => {
117
+ const PlanCardStory = () => {
118
+ const [checked, setChecked] = useState(true);
119
+
120
+ return (
121
+ <PlanCard
122
+ {...args}
123
+ checked={true}
124
+ onChange={() => setChecked(!checked)}
125
+ />
126
+ );
127
+ };
128
+
129
+ return <PlanCardStory />;
130
+ },
131
+ };
132
+
133
+ export const DefaultRadio: Story = {
134
+ args: {
135
+ title: "Basic Plan",
136
+ price: "$20",
137
+ per: "month",
138
+ size: "md",
139
+ headerTitle: "Subscribe to newsletter",
140
+ description:
141
+ "Get the latest updates, articles, and promotions delivered to your inbox.",
142
+ badgeContent: "limited time only",
143
+ id: "basic-plan",
144
+ checkboxIndex: 0,
145
+ type: "radio",
146
+ name: "plan-group", // important for radio
147
+ value: "basic-plan", // required for identifying the selected value
148
+ },
149
+ render: (args) => {
150
+ const PlanCardStory = () => {
151
+ const [checked, setChecked] = useState(false);
152
+
153
+ return (
154
+ <PlanCard
155
+ {...args}
156
+ checked={checked}
157
+ onCardClick={() => setChecked(true)}
158
+ onChange={() => setChecked(true)}
159
+ />
160
+ );
161
+ };
162
+
163
+ return <PlanCardStory />;
164
+ },
165
+ };
@@ -0,0 +1,32 @@
1
+ export const iconSizeStyle = {
2
+ sm: "16",
3
+ md: "20",
4
+ };
5
+ export const textSizeStyle = {
6
+ sm: "text-sm",
7
+ md: "text-md",
8
+ };
9
+ const labelBaseClass = `
10
+ md:w-3xl h-fit min-w-[343px] text-rtext-tertiary-600 rounded-2xl flex group gap-2 cursor-pointer
11
+ border-2 border-rborder-secondary rounded-2xl`;
12
+
13
+ export const planCardStyles = ({
14
+ checked,
15
+ disabled,
16
+ size,
17
+ hasHeader,
18
+ }: {
19
+ checked?: boolean;
20
+ disabled?: boolean;
21
+ hasHeader?: boolean;
22
+ size?: "md" | "sm";
23
+ }): { label: string } => {
24
+ return {
25
+ label: `${labelBaseClass} ${hasHeader ? "flex-col" : "p-4"} ${size && textSizeStyle[size]
26
+ } ${disabled
27
+ ? "bg-rbg-disabled-subtle"
28
+ : "group"
29
+ } ${checked ? "!border-rbg-brand-solid" : ""}`,
30
+ };
31
+ };
32
+
@@ -0,0 +1,54 @@
1
+ import { render, screen, fireEvent } from "@testing-library/react";
2
+ import { describe, it, expect, vi } from "vitest";
3
+ import { PlanCard } from ".";
4
+
5
+ describe("PlanCard", () => {
6
+ const defaultProps = {
7
+ id: "test-plan",
8
+ checkboxIndex: 0,
9
+ checked: false,
10
+ size: "sm" as const,
11
+ onChange: vi.fn(),
12
+ type: "checkbox" as const,
13
+ disabled: false,
14
+ price: "$20",
15
+ per: "month",
16
+ title: "Basic Plan",
17
+ description: "Best for starters",
18
+ };
19
+
20
+ it("renders the plan card with basic info", () => {
21
+ render(<PlanCard {...defaultProps} />);
22
+ expect(screen.getByText("Basic Plan")).toBeInTheDocument();
23
+ expect(screen.getByText("Best for starters")).toBeInTheDocument();
24
+ // expect(screen.getByText("$20")).toBeInTheDocument();
25
+ // expect(screen.getByText("month")).toBeInTheDocument();
26
+ });
27
+
28
+ it("fires onChange when clicked", () => {
29
+ render(<PlanCard {...defaultProps} />);
30
+ const label = screen.getByLabelText(/Basic Plan/i);
31
+ fireEvent.click(label);
32
+ expect(defaultProps.onChange).toHaveBeenCalled();
33
+ });
34
+
35
+ // it("disables interaction when disabled is true", () => {
36
+ // render(<PlanCard {...defaultProps} disabled={true} />);
37
+ // const label = screen.getByLabelText(/Basic Plan/i);
38
+ // expect(label).toHaveClass("bg-rbg-disabled-subtle");
39
+ // fireEvent.click(label);
40
+ // expect(defaultProps.onChange).not.toHaveBeenCalled();
41
+ // });
42
+
43
+ it("adds correct styles when checked", () => {
44
+ render(<PlanCard {...defaultProps} checked={true} />);
45
+ const wrapper = screen.getByTestId("plan-card-wrapper");
46
+ expect(wrapper.className).toMatch(/border-2/);
47
+ });
48
+
49
+ // it("renders with header if hasHeader is true", () => {
50
+ // render(<PlanCard {...defaultProps} hasHeader={true} />);
51
+ // const label = screen.getByLabelText(/Basic Plan/i);
52
+ // expect(label.className).toMatch(/flex-col/);
53
+ // });
54
+ });
@@ -0,0 +1,35 @@
1
+ import { ReactNode } from "react";
2
+
3
+ export const ringPadding = {
4
+ sm: "p-1",
5
+ md: "p-[5px]",
6
+ lg: "p-1.5",
7
+ };
8
+
9
+ export interface CommonCheckboxProps {
10
+ id?: string; // required string
11
+ checkboxIndex?: number; // required number
12
+ checked?: boolean; // required
13
+ size?: "sm" | "md";
14
+ onChange?: (event: React.ChangeEvent<HTMLInputElement>) => void; // required
15
+ inputProps?: object;
16
+ type?: "radio" | "checkbox";
17
+ disabled?: boolean;
18
+ }
19
+ export interface PlanCardProps extends CommonCheckboxProps {
20
+ value?: string;
21
+ price: string;
22
+ per: string;
23
+ title: string;
24
+ description: string;
25
+ hasHeader?: boolean;
26
+ headerTitle?: string;
27
+ badgeContent?: ReactNode;
28
+ defaultChecked?: boolean;
29
+ label?: string;
30
+ name?: string;
31
+ onClick?: () => void;
32
+ onCardClick?: () => void;
33
+ children?: React.ReactNode;
34
+ isIndeterminate?: boolean;
35
+ }
@@ -0,0 +1,53 @@
1
+ import { PlanCardBasic } from "@/components/ui/checkbox-group/plan-card/basic";
2
+ import { PlanCardWithHeader } from "@/components/ui/checkbox-group/plan-card/with-header";
3
+ import { planCardStyles } from "./_.style";
4
+ import { PlanCardProps } from "./_.types";
5
+
6
+ export const PlanCard = ({
7
+ hasHeader = false,
8
+ per,
9
+ size = "sm",
10
+ title = "",
11
+ description,
12
+ price,
13
+ badgeContent,
14
+ checked,
15
+ disabled,
16
+ type,
17
+ name,
18
+ value,
19
+ onChange,
20
+ onCardClick,
21
+ id,
22
+ checkboxIndex,
23
+ }: Readonly<PlanCardProps>) => {
24
+ const CardComponent = hasHeader ? PlanCardWithHeader : PlanCardBasic;
25
+ const styles = planCardStyles({
26
+ checked,
27
+ disabled,
28
+ hasHeader,
29
+ size,
30
+ });
31
+ return (
32
+ <label {...(process.env.NODE_ENV === "test" && {
33
+ "data-testid": "plan-card-wrapper",
34
+ })} onClick={onCardClick} htmlFor={id} tabIndex={checkboxIndex} className={styles.label}>
35
+ <CardComponent
36
+ type={type}
37
+ checkboxIndex={checkboxIndex}
38
+ checked={checked}
39
+ name={name}
40
+ per={per}
41
+ size={size}
42
+ title={title}
43
+ description={description}
44
+ price={price}
45
+ badgeContent={badgeContent}
46
+ id={id}
47
+ value={value}
48
+ disabled={disabled}
49
+ onChange={onChange}
50
+ />
51
+ </label>
52
+ );
53
+ }
@@ -0,0 +1,89 @@
1
+ import { Meta, StoryObj } from "@storybook/react-vite";
2
+ import { useState } from "react";
3
+ import { paymentCardProps } from "./_.types";
4
+ import { UserCard } from ".";
5
+
6
+ type ExtendedProps = paymentCardProps & {
7
+ forceFocus?: boolean;
8
+ };
9
+
10
+ const meta: Meta<ExtendedProps> = {
11
+ title: "Components/UserCard",
12
+ component: UserCard,
13
+ tags: ["autodocs"],
14
+ argTypes: {
15
+ size: {
16
+ options: ["sm", "md"],
17
+ control: { type: "radio" },
18
+ },
19
+ type: {
20
+ options: ["checkbox", "radio"],
21
+ control: { type: "radio" },
22
+ },
23
+ forceFocus: {
24
+ control: "boolean",
25
+ },
26
+ onChange: { action: "changed" },
27
+ cardOnclick: { action: "card clicked" },
28
+ },
29
+ };
30
+
31
+ export default meta;
32
+
33
+ const defaultArgs: ExtendedProps = {
34
+ id: "usercard-1",
35
+ checkboxIndex: 0,
36
+ description: "Sample user description",
37
+ userFullName: "Jane Doe",
38
+ username: "@janedoe",
39
+ checked: false,
40
+ disabled: false,
41
+ size: "sm",
42
+ type: "checkbox",
43
+ name: "userCard",
44
+ value: "1",
45
+ forceFocus: false,
46
+ };
47
+
48
+ //
49
+ // ✅ This wrapper gives it real interactive behavior in Storybook.
50
+ //
51
+ const InteractiveStory = (args: ExtendedProps) => {
52
+ const [checked, setChecked] = useState(args.checked ?? false);
53
+
54
+ const handleChange = () => {
55
+ setChecked((prev) => !prev);
56
+ };
57
+
58
+ return (
59
+ <UserCard
60
+ {...args}
61
+ checked={checked}
62
+ onChange={handleChange}
63
+ cardOnclick={handleChange}
64
+ />
65
+ );
66
+ };
67
+
68
+ export const Default: StoryObj<ExtendedProps> = {
69
+ args: {
70
+ ...defaultArgs,
71
+ },
72
+ render: (args) => <InteractiveStory {...args} />,
73
+ };
74
+
75
+ export const Checked: StoryObj<ExtendedProps> = {
76
+ args: {
77
+ ...defaultArgs,
78
+ checked: true,
79
+ },
80
+ render: (args) => <InteractiveStory {...args} />,
81
+ };
82
+ export const RadioUserCard: StoryObj<ExtendedProps> = {
83
+ args: {
84
+ ...defaultArgs,
85
+ checked: true,
86
+ type: "radio",
87
+ },
88
+ render: (args) => <InteractiveStory {...args} />,
89
+ };
@@ -0,0 +1,42 @@
1
+ const iconSizeStyle = {
2
+ sm: "16",
3
+ md: "20",
4
+ };
5
+ const textSizeStyle = {
6
+ sm: "text-sm",
7
+ md: "text-md",
8
+ };
9
+ const avatarSizeStyle = {
10
+ sm: "w-8 h-8",
11
+ md: "w-10 h-10",
12
+ };
13
+ const labelBaseClass = `
14
+ min-w-[343px] md:w-3xl rounded-xl flex group gap-2 cursor-pointer
15
+ border-2 border-rborder-secondary rounded-2xl text-rtext-tertiary-600`;
16
+
17
+ export const paymentCardStyles = ({
18
+ checked,
19
+ disabled,
20
+ size,
21
+ hasHeader,
22
+ }: {
23
+ checked?: boolean;
24
+ disabled?: boolean;
25
+ hasHeader?: boolean;
26
+ size?: "md" | "sm";
27
+ }) => {
28
+ return {
29
+ label: ` ${labelBaseClass} ${hasHeader ? "flex-col" : "p-4"} ${size && textSizeStyle[size]
30
+ } ${disabled
31
+ ? "bg-rbg-disabled-subtle"
32
+ : "group"
33
+ } ${checked ? "!border-rbg-brand-solid" : ""}`,
34
+ };
35
+ };
36
+
37
+ export {
38
+ iconSizeStyle,
39
+ textSizeStyle,
40
+ avatarSizeStyle,
41
+ labelBaseClass,
42
+ };
@@ -0,0 +1,66 @@
1
+ import { fireEvent, render, screen } from "@testing-library/react";
2
+ import { describe, expect, it, vi } from "vitest";
3
+ import { UserCard } from ".";
4
+ import { paymentCardProps } from "./_.types";
5
+
6
+ const defaultProps: paymentCardProps = {
7
+ id: "user-card",
8
+ description: "Test description",
9
+ username: "@johndoe",
10
+ userFullName: "John Doe",
11
+ checked: false,
12
+ onChange: vi.fn(),
13
+ size: "sm",
14
+ type: "checkbox",
15
+ };
16
+
17
+ describe("UserCard (PaymentCard) Component", () => {
18
+ it("renders avatar image with correct alt", () => {
19
+ render(<UserCard {...defaultProps} />);
20
+ const img = screen.getByAltText("avatar");
21
+ expect(img).toBeInTheDocument();
22
+ expect(img).toHaveAttribute("src", "/Avatar.png");
23
+ });
24
+
25
+ it("renders full name and username", () => {
26
+ render(<UserCard {...defaultProps} />);
27
+ expect(screen.getByText("John Doe")).toBeInTheDocument();
28
+ expect(screen.getByText("@johndoe")).toBeInTheDocument();
29
+ });
30
+
31
+ it("calls onChange when checkbox is clicked", () => {
32
+ render(<UserCard {...defaultProps} />);
33
+ const checkbox = screen.getByRole("checkbox");
34
+ fireEvent.click(checkbox);
35
+ expect(defaultProps.onChange).toHaveBeenCalledTimes(1);
36
+ });
37
+
38
+ it("handles card click if cardOnclick is passed", () => {
39
+ const cardOnclick = vi.fn();
40
+ render(<UserCard {...defaultProps} cardOnclick={cardOnclick} />);
41
+ const cardWrapper = screen.getByText("John Doe").closest("div");
42
+ fireEvent.click(cardWrapper!);
43
+ expect(cardOnclick).toHaveBeenCalledTimes(2);
44
+ });
45
+
46
+ it("renders Radio component when type is radio", () => {
47
+ render(<UserCard {...defaultProps} type="radio" />);
48
+ const radioButton = screen.getByRole("radio");
49
+ expect(radioButton).toBeInTheDocument();
50
+ });
51
+
52
+ it("calls onChange when radio is clicked", () => {
53
+ const onChange = vi.fn();
54
+ render(
55
+ <UserCard
56
+ {...defaultProps}
57
+ type="radio"
58
+ onChange={onChange} // pass the mock onChange function
59
+ />
60
+ );
61
+
62
+ const radioButton = screen.getByRole("radio");
63
+ fireEvent.click(radioButton);
64
+ expect(onChange).toHaveBeenCalledTimes(1);
65
+ });
66
+ });
@@ -0,0 +1,26 @@
1
+
2
+ export const ringPadding = {
3
+ sm: "p-1",
4
+ md: "p-[5px]",
5
+ lg: "p-1.5",
6
+ };
7
+
8
+ export interface CommonCheckboxProps {
9
+ id?: string; // required string
10
+ checkboxIndex?: number; // required number
11
+ checked?: boolean; // required
12
+ size?: "sm" | "md";
13
+ onChange?: (event: React.ChangeEvent<HTMLInputElement>) => void; // required
14
+ inputProps?: object;
15
+ type?: "radio" | "checkbox";
16
+ disabled?: boolean;
17
+ }
18
+ export interface paymentCardProps extends CommonCheckboxProps {
19
+ description: string;
20
+ value?: string;
21
+ name?: string;
22
+ userFullName: string;
23
+ cardOnclick?: () => void;
24
+ inputProps?: object;
25
+ username: string;
26
+ }
@@ -0,0 +1,75 @@
1
+ import { Checkbox } from "@/components/Checkbox";
2
+
3
+ import { Radio } from "@/components/Radio";
4
+ import { avatarSizeStyle, paymentCardStyles } from "./_.style";
5
+ import { paymentCardProps } from "./_.types";
6
+
7
+ export const UserCard = ({
8
+ size = "sm",
9
+ description,
10
+ checked,
11
+ disabled,
12
+ id,
13
+ value = "",
14
+ type = "checkbox",
15
+ onChange,
16
+ name = "",
17
+ inputProps,
18
+ username,
19
+ checkboxIndex,
20
+ userFullName,
21
+ cardOnclick,
22
+ }: Readonly<paymentCardProps>) => {
23
+ const styles = paymentCardStyles({
24
+ checked,
25
+ disabled,
26
+ hasHeader: false,
27
+ size,
28
+ });
29
+ return (
30
+ <label onClick={cardOnclick} htmlFor={id} tabIndex={checkboxIndex} className={styles.label}>
31
+ <img
32
+ src="/Avatar.png"
33
+ alt="avatar"
34
+ role="avatar"
35
+ className={`rounded-full ${avatarSizeStyle[size]}`}
36
+ />
37
+ <div className="flex flex-col gap-3 grow justify-between">
38
+ <div className="flex flex-col">
39
+ <div className="flex gap-1">
40
+ <h3 className="font-medium text-foreground text-rtext-secondary-700">
41
+ {userFullName}
42
+ </h3>
43
+ <span className="text-rtext-tertiary-600">{username}</span>
44
+ </div>
45
+ <span className="text-rtext-tertiary-600">{description}</span>
46
+ </div>
47
+ </div>
48
+ <div className="flex items-start gap-2">
49
+ {type === "radio" ? (
50
+ <Radio
51
+ onChange={onChange}
52
+ checked={checked}
53
+ disabled={disabled}
54
+ value={value}
55
+ name={name}
56
+ size={size}
57
+ id={id}
58
+ {...inputProps}
59
+ />
60
+ ) : (
61
+ <Checkbox
62
+ {...inputProps}
63
+ disabled={disabled}
64
+ id={id}
65
+ name={name}
66
+ checked={checked}
67
+ onChange={onChange}
68
+ size={size}
69
+ value={value}
70
+ />
71
+ )}
72
+ </div>
73
+ </label>
74
+ );
75
+ }