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,96 @@
1
+ import type { Meta, StoryObj } from "@storybook/react-vite";
2
+ import { Checkbox } from "."; // adjust the path if needed
3
+
4
+ const meta: Meta<typeof Checkbox> = {
5
+ title: "Components/Checkbox",
6
+ component: Checkbox,
7
+ tags: ["autodocs"],
8
+ argTypes: {
9
+ onClick: { action: "clicked" },
10
+ onChange: { action: "changed" },
11
+ size: {
12
+ control: { type: "select" },
13
+ options: ["sm", "md", "lg"],
14
+ },
15
+ disabled: { control: "boolean" },
16
+ defaultChecked: { control: "boolean" },
17
+ isIndeterminate: { control: "boolean" },
18
+ },
19
+ args: {
20
+ id: "default-checkbox",
21
+ name: "default-checkbox",
22
+ children: "Accept Terms",
23
+ size: "md",
24
+ disabled: false,
25
+ defaultChecked: false,
26
+ isIndeterminate: false,
27
+ },
28
+ };
29
+
30
+ export default meta;
31
+
32
+ type Story = StoryObj<typeof Checkbox>;
33
+
34
+ export const Default: Story = {};
35
+
36
+ export const DefaultChecked: Story = {
37
+ args: {
38
+ defaultChecked: true,
39
+ id: "checked-checkbox",
40
+ name: "checked-checkbox",
41
+ children: "Pre-checked Option",
42
+ },
43
+ };
44
+
45
+ export const Disabled: Story = {
46
+ args: {
47
+ disabled: true,
48
+ id: "disabled-checkbox",
49
+ name: "disabled-checkbox",
50
+ children: "Disabled Checkbox",
51
+ },
52
+ };
53
+
54
+ export const Indeterminate: Story = {
55
+ args: {
56
+ isIndeterminate: true,
57
+ id: "indeterminate-checkbox",
58
+ name: "indeterminate-checkbox",
59
+ children: "Indeterminate Checkbox",
60
+ },
61
+ };
62
+
63
+ export const Sizes: Story = {
64
+ render: () => (
65
+ <div className="flex flex-col gap-3">
66
+ <Checkbox id="checkbox-sm" name="checkbox-sm" size="sm">
67
+ Small
68
+ </Checkbox>
69
+ <Checkbox id="checkbox-md" name="checkbox-md" size="md">
70
+ Medium
71
+ </Checkbox>
72
+ <Checkbox id="checkbox-lg" name="checkbox-lg" size="lg">
73
+ Large
74
+ </Checkbox>
75
+ </div>
76
+ ),
77
+ };
78
+
79
+ export const WithName: Story = {
80
+ render: () => (
81
+ <div className="flex flex-col gap-3">
82
+ <div className="flex items-center gap-2">
83
+ <Checkbox id="checkbox-name-1" name="checkbox-name-1" />
84
+ <label htmlFor="checkbox-name-1" className="ml-2">
85
+ Checkbox controlled by name
86
+ </label>
87
+ </div>
88
+ <div className="flex items-center gap-2">
89
+ <Checkbox id="checkbox-name-2" name="checkbox-name-2" size="lg" />
90
+ <label htmlFor="checkbox-name-2" className="ml-2">
91
+ Large checkbox with name
92
+ </label>
93
+ </div>
94
+ </div>
95
+ ),
96
+ };
@@ -0,0 +1,24 @@
1
+ import { cva } from "class-variance-authority";
2
+
3
+ const baseStyles =
4
+ "border-rborder-primary rounded-xs border-[1px] peer-checked:border-rbg-brand-solid peer-checked:bg-rbg-brand-solid flex items-center justify-center";
5
+
6
+ const sizeStyle = {
7
+ sm: "w-3.5 h-3.5",
8
+ md: "w-4 h-4",
9
+ lg: "w-4.5 h-4.5",
10
+ };
11
+
12
+ export const checkboxVariants = cva(baseStyles, {
13
+ variants: {
14
+ size: sizeStyle,
15
+ disabled: {
16
+ true: " bg-rbg-disabled-subtle cursor-auto",
17
+ false: " group cursor-pointer",
18
+ undefined: "group cursor-pointer",
19
+ },
20
+ },
21
+ defaultVariants: {
22
+ size: "sm",
23
+ },
24
+ });
@@ -0,0 +1,85 @@
1
+ import "@testing-library/jest-dom";
2
+ import { fireEvent, render, screen } from "@testing-library/react";
3
+ import { describe, expect, it, vi } from "vitest";
4
+ import { Checkbox } from "."; // update the import path to match your project structure
5
+
6
+ describe("Checkbox Component", () => {
7
+ it("renders with default props", () => {
8
+ render(<Checkbox id="checkbox-default" name="checkbox-default" />);
9
+ const checkbox = screen.getByTestId("checkbox");
10
+ expect(checkbox).toBeInTheDocument();
11
+ expect(checkbox).not.toBeChecked();
12
+ });
13
+
14
+ it("renders as checked when `checked` is true", () => {
15
+ render(<Checkbox id="checkbox-checked" checked name="checkbox-checked" />);
16
+ const checkbox = screen.getByTestId("checkbox");
17
+ expect(checkbox).toBeChecked();
18
+ });
19
+
20
+ it("calls onChange when clicked", () => {
21
+ const onChange = vi.fn();
22
+ render(<Checkbox id="checkbox-onchange" onChange={onChange} name="checkbox-onchange" />);
23
+ const checkbox = screen.getByTestId("checkbox");
24
+ fireEvent.click(checkbox);
25
+ expect(onChange).toHaveBeenCalledTimes(1);
26
+ });
27
+
28
+ it("does NOT call onChange when disabled", () => {
29
+ const onChange = vi.fn();
30
+ render(<Checkbox id="checkbox-disabled" disabled onChange={onChange} name="checkbox-disabled" />);
31
+ const checkbox = screen.getByTestId("checkbox");
32
+ fireEvent.click(checkbox);
33
+ expect(onChange).not.toHaveBeenCalled();
34
+ });
35
+
36
+ it("renders children and toggles checkbox on child click (manual toggle)", () => {
37
+ render(
38
+ <Checkbox id="checkbox-children" defaultChecked={false} name="checkbox-children">
39
+ <span>Click label</span>
40
+ </Checkbox>
41
+ );
42
+ const label = screen.getByText("Click label");
43
+ expect(label).toBeInTheDocument();
44
+ fireEvent.click(label);
45
+ const checkbox = screen.getByTestId("checkbox");
46
+ expect(checkbox).toBeInTheDocument();
47
+ });
48
+
49
+ it("renders minus icon when indeterminate is true", () => {
50
+ render(<Checkbox id="checkbox-minus" isIndeterminate name="checkbox-minus" />);
51
+ const minus = screen.getByTestId("minus-icon");
52
+ expect(minus).toBeInTheDocument();
53
+ });
54
+
55
+ it("renders tick icon when not indeterminate", () => {
56
+ render(<Checkbox id="checkbox-tick" name="checkbox-tick" />);
57
+ const tick = screen.getByTestId("tickbox-icon");
58
+ expect(tick).toBeInTheDocument();
59
+ });
60
+
61
+ it("applies correct size variant classes", () => {
62
+ const { rerender, container } = render(
63
+ <Checkbox id="checkbox-size-sm" size="sm" name="checkbox-size-sm" />
64
+ );
65
+
66
+ let label = container.querySelector("label");
67
+ expect(label?.className).toMatch(/w-3\.5/);
68
+
69
+ rerender(<Checkbox id="checkbox-size-md" size="md" name="checkbox-size-md" />);
70
+ label = container.querySelector("label");
71
+ expect(label?.className).toMatch(/w-4/);
72
+
73
+ rerender(<Checkbox id="checkbox-size-lg" size="lg" name="checkbox-size-lg" />);
74
+ label = container.querySelector("label");
75
+ expect(label?.className).toMatch(/w-4\.5/);
76
+ });
77
+
78
+ it("calls onClick prop when label is clicked", () => {
79
+ const handleClick = vi.fn();
80
+ render(<Checkbox id="checkbox-click" onClick={handleClick} name="checkbox-click" />);
81
+ const checkbox = screen.getByTestId("checkbox");
82
+ fireEvent.click(checkbox);
83
+ expect(handleClick).toHaveBeenCalledTimes(1);
84
+ });
85
+ });
@@ -0,0 +1,23 @@
1
+ export const ringPadding = {
2
+ sm: "p-1",
3
+ md: "p-[5px]",
4
+ lg: "p-1.5",
5
+ };
6
+ export interface CommonCheckboxProps {
7
+ id?: string; // required string
8
+ name: string; // required string for form control
9
+ checkboxIndex?: number; // required number
10
+ checked?: boolean; // required
11
+ onChange?: (event: React.ChangeEvent<HTMLInputElement>) => void; // required
12
+ size?: "sm" | "md" | "lg";
13
+ type?: "radio" | "checkbox";
14
+ disabled?: boolean;
15
+ }
16
+ export interface CheckboxProps extends CommonCheckboxProps {
17
+ defaultChecked?: boolean;
18
+ label?: string;
19
+ value?: string;
20
+ onClick?: () => void;
21
+ children?: React.ReactNode;
22
+ isIndeterminate?: boolean;
23
+ }
@@ -0,0 +1,93 @@
1
+ import Minus from "@/icons/general/minus";
2
+ import TickBox from "@/icons/general/tick-box";
3
+ import { cn } from "@/lib/utils";
4
+ import { useEffect, useRef } from "react";
5
+ import { checkboxVariants } from "./_.style";
6
+ import { CheckboxProps, ringPadding } from "./_.types";
7
+
8
+ export const Checkbox = ({
9
+ size = "sm",
10
+ children,
11
+ id,
12
+ disabled = false,
13
+ checkboxIndex = 1,
14
+ isIndeterminate = false,
15
+ checked,
16
+ onChange,
17
+ onClick,
18
+ defaultChecked,
19
+ name,
20
+ label,
21
+ ...props
22
+ }: Readonly<CheckboxProps>) => {
23
+ const checkboxRef = useRef<HTMLInputElement | null>(null);
24
+
25
+ useEffect(() => {
26
+ if (checkboxRef.current) {
27
+ checkboxRef.current.indeterminate = isIndeterminate;
28
+ }
29
+ }, [isIndeterminate]);
30
+
31
+ // for handling label text click
32
+ function handleCheckText() {
33
+ if (checkboxRef.current && !disabled) {
34
+ checkboxRef.current.click();
35
+ checkboxRef.current.focus();
36
+ }
37
+ }
38
+ const strokeColor = disabled ? "#D0D5DD" : "white";
39
+ let icon = null as React.ReactNode;
40
+ if (disabled) {
41
+ if (checked) {
42
+ icon = isIndeterminate ? (
43
+ <Minus stroke={strokeColor} />
44
+ ) : (
45
+ <TickBox stroke={strokeColor} />
46
+ );
47
+ }
48
+ } else {
49
+ icon = isIndeterminate ? (
50
+ <Minus stroke={strokeColor} />
51
+ ) : (
52
+ <TickBox stroke={strokeColor} />
53
+ );
54
+ }
55
+ return (
56
+ <div className="flex items-center cursor-pointer gap-2 justify-start">
57
+ <input
58
+ type="checkbox"
59
+ data-testid="checkbox"
60
+ id={id}
61
+ ref={checkboxRef}
62
+ onChange={(e) => !disabled && onChange && onChange(e)}
63
+ onClick={onClick}
64
+ disabled={disabled}
65
+ checked={checked}
66
+ defaultChecked={defaultChecked}
67
+ className="peer hidden"
68
+ {...props}
69
+ />
70
+ <label
71
+ htmlFor={id ?? name}
72
+ tabIndex={checkboxIndex}
73
+ className={cn(
74
+ checkboxVariants({ size, disabled }),
75
+ `${disabled && "!bg-rbg-disabled !border-rborder-primary"} `
76
+ )}
77
+ >
78
+ <div
79
+ className={`${ringPadding[size]} ${
80
+ !disabled && "group-focus-within:border-2 border-rbg-brand-solid "
81
+ } flex items-center justify-center rounded-md`}
82
+ >
83
+ {icon}
84
+ </div>
85
+ </label>
86
+ {(children ?? label) && (
87
+ <div className="leading-none" onClick={() => handleCheckText()}>
88
+ {children ?? label}
89
+ </div>
90
+ )}
91
+ </div>
92
+ );
93
+ };
@@ -0,0 +1,104 @@
1
+ import IconWrapper from "@/components/ui/icon-wrapper";
2
+ import MasterCard from "@/icons/general/master-card";
3
+ import { Meta, StoryFn } from "@storybook/react-vite";
4
+ import { ReactNode } from "react";
5
+ import { PaymentCard } from ".";
6
+
7
+ // Define the types for the args
8
+ interface PaymentCardArgs {
9
+ checked?: boolean;
10
+ disabled?: boolean;
11
+ size?: "sm" | "md";
12
+ type?: "radio" | "checkbox";
13
+ hasHeader?: boolean;
14
+ title: string;
15
+ description: string;
16
+ value?: string;
17
+ footer: ReactNode;
18
+ icon: ReactNode;
19
+ cardOnclick?: () => void;
20
+ }
21
+
22
+ // Default export for Storybook to associate this with the component
23
+ export default {
24
+ title: "Components/PaymentCard",
25
+ component: PaymentCard,
26
+ argTypes: {
27
+ checked: { control: "boolean" },
28
+ disabled: { control: "boolean" },
29
+ size: {
30
+ control: {
31
+ type: "select",
32
+ options: ["sm", "md"],
33
+ },
34
+ },
35
+ hasHeader: { control: "boolean" },
36
+ title: { control: "text" },
37
+ description: { control: "text" },
38
+ value: { control: "text" },
39
+ footer: { control: "text" },
40
+ icon: { control: "text" },
41
+ },
42
+ } as Meta;
43
+
44
+ // Template function for stories using StoryFn
45
+ const Template: StoryFn<PaymentCardArgs> = (args) => {
46
+ const footer: ReactNode = (
47
+ <div className="text-xs text-gray-500">Footer Content</div>
48
+ );
49
+ const icon: ReactNode = (
50
+ <IconWrapper className="!h-8 !w-[46px]" data-testid="icon-wrapper">
51
+ <MasterCard />
52
+ </IconWrapper>
53
+ );
54
+
55
+ return (
56
+ <PaymentCard
57
+ title={args.title}
58
+ description={args.description}
59
+ value={args.value}
60
+ footer={footer}
61
+ icon={icon}
62
+ checked={args.checked}
63
+ type={args.type}
64
+ size={args.size}
65
+ disabled={args.disabled}
66
+ onChange={() => { }}
67
+ />
68
+ );
69
+ };
70
+
71
+ // Story with Header
72
+ export const Default = Template.bind({});
73
+ Default.args = {
74
+ title: "Premium Plan",
75
+ description: "Description for the premium plan",
76
+ value: "premium-plan",
77
+ checked: true,
78
+ size: "md",
79
+ disabled: false,
80
+ type: "checkbox",
81
+ hasHeader: true, // This will enable the header in the PaymentCard
82
+ };
83
+
84
+ // Story with Disabled State
85
+ export const Disabled = Template.bind({});
86
+ Disabled.args = {
87
+ title: "Disabled Plan",
88
+ description: "Description for the disabled plan",
89
+ value: "disabled-plan",
90
+ checked: false,
91
+ size: "md",
92
+ disabled: true, // This will disable the PaymentCard
93
+ hasHeader: false,
94
+ };
95
+ // Story with Header
96
+ export const RadioCard = Template.bind({});
97
+ RadioCard.args = {
98
+ title: "Premium Plan",
99
+ description: "Description for the premium plan",
100
+ value: "premium-plan",
101
+ size: "md",
102
+ disabled: false,
103
+ type: "radio",
104
+ };
@@ -0,0 +1,28 @@
1
+ const textSizeStyle = {
2
+ sm: "text-sm",
3
+ md: "text-md",
4
+ };
5
+ const labelBaseClass = `
6
+ rounded-2xl md:w-3xl text-md h-fit min-w-[343px] text-rtext-tertiary-600 flex gap-2 cursor-pointer
7
+ border-2 border-rborder-secondary rounded-2xl`;
8
+
9
+ export const paymentCardStyles = ({
10
+ checked,
11
+ disabled,
12
+ size,
13
+ hasHeader,
14
+ }: {
15
+ checked?: boolean;
16
+ disabled?: boolean;
17
+ hasHeader?: boolean;
18
+ size?: "md" | "sm";
19
+ }) => {
20
+ return {
21
+ label: ` ${labelBaseClass} ${hasHeader ? "flex-col" : "p-4"} ${size && textSizeStyle[size]
22
+ } ${disabled
23
+ ? "bg-rbg-disabled-subtle"
24
+ : "group"
25
+ } ${checked ? "!border-rbg-brand-solid" : ""}`,
26
+ };
27
+ };
28
+
@@ -0,0 +1,58 @@
1
+ import { render, screen, fireEvent } from "@testing-library/react";
2
+ import { describe, it, expect, vi } from "vitest";
3
+ import { PaymentCard } from ".";
4
+
5
+ describe("PaymentCard", () => {
6
+ const baseProps = {
7
+ id: "test-card",
8
+ title: "Test Title",
9
+ description: "Test Description",
10
+ checked: true,
11
+ value: "test-value",
12
+ name: "test-name",
13
+ footer: <div data-testid="footer">Footer Content</div>,
14
+ icon: <span data-testid="icon">Icon</span>,
15
+ };
16
+
17
+ it("renders title and description", () => {
18
+ render(<PaymentCard {...baseProps} />);
19
+ expect(screen.getByText("Test Title")).toBeInTheDocument();
20
+ expect(screen.getByText("Test Description")).toBeInTheDocument();
21
+ });
22
+
23
+ it("renders a checkbox by default", () => {
24
+ render(<PaymentCard {...baseProps} type="checkbox" />);
25
+ expect(screen.getByRole("checkbox")).toBeInTheDocument();
26
+ });
27
+
28
+ it("renders a radio when type is 'radio'", () => {
29
+ render(<PaymentCard {...baseProps} type="radio" />);
30
+ expect(screen.getByRole("radio")).toBeInTheDocument();
31
+ });
32
+
33
+ it("calls onChange when input is clicked", () => {
34
+ const onChange = vi.fn();
35
+ render(<PaymentCard {...baseProps} onChange={onChange} />);
36
+ fireEvent.click(screen.getByRole("checkbox"));
37
+ expect(onChange).toHaveBeenCalled();
38
+ });
39
+
40
+ it("calls cardOnclick when the card is clicked", () => {
41
+ const cardOnclick = vi.fn();
42
+ render(<PaymentCard {...baseProps} cardOnclick={cardOnclick} />);
43
+ // Target the label element itself, not just the title
44
+ fireEvent.click(screen.getByText("Test Title"));
45
+ expect(cardOnclick).toHaveBeenCalled();
46
+ });
47
+
48
+ it("disables input when disabled prop is true", () => {
49
+ render(<PaymentCard {...baseProps} disabled={true} />);
50
+ expect(screen.getByRole("checkbox")).toBeDisabled();
51
+ });
52
+
53
+ it("renders custom icon and footer", () => {
54
+ render(<PaymentCard {...baseProps} />);
55
+ expect(screen.getByTestId("icon")).toBeInTheDocument();
56
+ expect(screen.getByTestId("footer")).toBeInTheDocument();
57
+ });
58
+ });
@@ -0,0 +1,28 @@
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
+
20
+ export interface paymentCardProps extends CommonCheckboxProps {
21
+ title: string;
22
+ description: string;
23
+ value?: string;
24
+ footer: ReactNode;
25
+ icon: ReactNode;
26
+ name?: string;
27
+ cardOnclick?: () => void;
28
+ }
@@ -0,0 +1,71 @@
1
+ import { Checkbox } from "@/components/Checkbox";
2
+ import { Radio } from "@/components/Radio";
3
+ import { paymentCardStyles } from "./_.style";
4
+ import { paymentCardProps } from "./_.types";
5
+
6
+ export const PaymentCard = ({
7
+ size = "sm",
8
+ title = "",
9
+ description,
10
+ checked,
11
+ disabled,
12
+ id,
13
+ value = "",
14
+ footer,
15
+ icon,
16
+ type = "checkbox",
17
+ onChange,
18
+ name = "",
19
+ cardOnclick,
20
+ inputProps,
21
+ checkboxIndex,
22
+ }: Readonly<paymentCardProps>) => {
23
+ const styles = paymentCardStyles({
24
+ checked,
25
+ disabled,
26
+ size,
27
+ });
28
+ return (
29
+ <label
30
+ onClick={cardOnclick}
31
+ htmlFor={id}
32
+ tabIndex={checkboxIndex}
33
+ className={styles.label}
34
+ >
35
+ {icon}
36
+ <div className="flex flex-col gap-3 grow justify-between">
37
+ <div className="flex flex-col">
38
+ <h3 className="font-medium text-foreground text-rtext-secondary-700">
39
+ {title}
40
+ </h3>
41
+
42
+ <span className="text-rtext-tertiary-600">{description}</span>
43
+ </div>
44
+ {footer}
45
+ </div>
46
+ <div className="flex items-start gap-2">
47
+ {type === "radio" ? (
48
+ <Radio
49
+ onChange={onChange}
50
+ checked={checked}
51
+ disabled={disabled}
52
+ value={value}
53
+ name={name}
54
+ size={size}
55
+ id={id}
56
+ {...inputProps}
57
+ />
58
+ ) : (
59
+ <Checkbox
60
+ {...inputProps}
61
+ disabled={disabled}
62
+ id={id}
63
+ name={name}
64
+ onChange={onChange}
65
+ size={size}
66
+ />
67
+ )}
68
+ </div>
69
+ </label>
70
+ );
71
+ };