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.
- package/.gitlab-ci.yml +43 -0
- package/.storybook/main.ts +15 -0
- package/.storybook/preview.ts +15 -0
- package/README.md +254 -0
- package/components.json +21 -0
- package/dist/Avatar.png +0 -0
- package/dist/assets/index-CCq7hmG3.js +186 -0
- package/dist/assets/index-Mg-hjQGu.css +1 -0
- package/dist/index.html +15 -0
- package/dist/test.png +0 -0
- package/dist/vite.svg +1 -0
- package/eslint.config.js +29 -0
- package/index.html +14 -0
- package/package.json +102 -0
- package/postcss.config.mjs +11 -0
- package/rollup.config.mjs +55 -0
- package/src/assets/react.svg +1 -0
- package/src/assets/style/animation.css +27 -0
- package/src/assets/style/box-shadow.css +25 -0
- package/src/assets/style/colors.css +402 -0
- package/src/assets/style/dark-theme.css +288 -0
- package/src/assets/style/font-size.css +14 -0
- package/src/assets/style/gradient.css +3 -0
- package/src/assets/style/index.css +12 -0
- package/src/assets/style/light-theme.css +148 -0
- package/src/assets/style/line-height.css +13 -0
- package/src/assets/style/max-width.css +5 -0
- package/src/assets/style/radius.css +13 -0
- package/src/assets/style/utility-colors.css +166 -0
- package/src/components/Accordion/_.stories.tsx +75 -0
- package/src/components/Accordion/_.test.tsx +77 -0
- package/src/components/Accordion/index.tsx +47 -0
- package/src/components/Accordion/type.ts +24 -0
- package/src/components/Avatar/_.stories.tsx +179 -0
- package/src/components/Avatar/_.style.ts +40 -0
- package/src/components/Avatar/_.test.tsx +150 -0
- package/src/components/Avatar/_.types.ts +66 -0
- package/src/components/Avatar/index.tsx +63 -0
- package/src/components/Badge/_.stories.tsx +75 -0
- package/src/components/Badge/_.style.ts +53 -0
- package/src/components/Badge/_.test.tsx +27 -0
- package/src/components/Badge/_.types.ts +11 -0
- package/src/components/Badge/index.tsx +42 -0
- package/src/components/Breadcrumbs/_.stories.tsx +95 -0
- package/src/components/Breadcrumbs/_.test.tsx +29 -0
- package/src/components/Breadcrumbs/_.type.ts +15 -0
- package/src/components/Breadcrumbs/index.tsx +103 -0
- package/src/components/Button/_.stories.tsx +85 -0
- package/src/components/Button/_.style.ts +56 -0
- package/src/components/Button/_.test.tsx +103 -0
- package/src/components/Button/_.types.ts +14 -0
- package/src/components/Button/index.tsx +70 -0
- package/src/components/Checkbox/_.stories.tsx +96 -0
- package/src/components/Checkbox/_.style.ts +24 -0
- package/src/components/Checkbox/_.test.tsx +85 -0
- package/src/components/Checkbox/_.types.ts +23 -0
- package/src/components/Checkbox/index.tsx +93 -0
- package/src/components/CheckboxGroup/PaymentCard/_.stories.tsx +104 -0
- package/src/components/CheckboxGroup/PaymentCard/_.style.ts +28 -0
- package/src/components/CheckboxGroup/PaymentCard/_.test.tsx +58 -0
- package/src/components/CheckboxGroup/PaymentCard/_.types.ts +28 -0
- package/src/components/CheckboxGroup/PaymentCard/index.tsx +71 -0
- package/src/components/CheckboxGroup/PlanCard/_.stories.tsx +165 -0
- package/src/components/CheckboxGroup/PlanCard/_.style.ts +32 -0
- package/src/components/CheckboxGroup/PlanCard/_.test.tsx +54 -0
- package/src/components/CheckboxGroup/PlanCard/_.types.ts +35 -0
- package/src/components/CheckboxGroup/PlanCard/index.tsx +53 -0
- package/src/components/CheckboxGroup/UserCard/_.stories.tsx +89 -0
- package/src/components/CheckboxGroup/UserCard/_.style.ts +42 -0
- package/src/components/CheckboxGroup/UserCard/_.test.tsx +66 -0
- package/src/components/CheckboxGroup/UserCard/_.types.ts +26 -0
- package/src/components/CheckboxGroup/UserCard/index.tsx +75 -0
- package/src/components/Dropdown/_.stories.tsx +180 -0
- package/src/components/Dropdown/_.style.ts +108 -0
- package/src/components/Dropdown/_.test.tsx +334 -0
- package/src/components/Dropdown/_.types.ts +12 -0
- package/src/components/Dropdown/index.tsx +130 -0
- package/src/components/FileUpload/_.stories.tsx +74 -0
- package/src/components/FileUpload/_.style.ts +0 -0
- package/src/components/FileUpload/_.test.tsx +222 -0
- package/src/components/FileUpload/_.types.ts +53 -0
- package/src/components/FileUpload/index.tsx +44 -0
- package/src/components/ImageMagnify/_.stories.tsx +226 -0
- package/src/components/ImageMagnify/_.style.ts +109 -0
- package/src/components/ImageMagnify/_.types.ts +44 -0
- package/src/components/ImageMagnify/index.tsx +204 -0
- package/src/components/Input/_.stories.tsx +177 -0
- package/src/components/Input/_.style.ts +79 -0
- package/src/components/Input/_.test.tsx +146 -0
- package/src/components/Input/_.types.ts +66 -0
- package/src/components/Input/index.tsx +231 -0
- package/src/components/InputTags/_.stories.tsx +51 -0
- package/src/components/InputTags/_.style.ts +28 -0
- package/src/components/InputTags/_.test.tsx +123 -0
- package/src/components/InputTags/_.types.ts +26 -0
- package/src/components/InputTags/index.tsx +140 -0
- package/src/components/Message/_.stories.tsx +79 -0
- package/src/components/Message/_.style.ts +87 -0
- package/src/components/Message/_.test.tsx +73 -0
- package/src/components/Message/_.types.ts +13 -0
- package/src/components/Message/index.tsx +57 -0
- package/src/components/Metric/_.stories.tsx +142 -0
- package/src/components/Metric/_.style.ts +14 -0
- package/src/components/Metric/_.test.tsx +166 -0
- package/src/components/Metric/_.types.ts +18 -0
- package/src/components/Metric/index.tsx +100 -0
- package/src/components/Modal/_.stories.tsx +93 -0
- package/src/components/Modal/_.style.ts +31 -0
- package/src/components/Modal/_.test.tsx +90 -0
- package/src/components/Modal/_.types.ts +14 -0
- package/src/components/Modal/index.tsx +82 -0
- package/src/components/Pagination/_.stories.tsx +118 -0
- package/src/components/Pagination/_.test.tsx +51 -0
- package/src/components/Pagination/index.tsx +256 -0
- package/src/components/Pagination/type.ts +48 -0
- package/src/components/PriceSlider/_.stories.tsx +107 -0
- package/src/components/PriceSlider/_.test.tsx +63 -0
- package/src/components/PriceSlider/_.type.tsx +19 -0
- package/src/components/PriceSlider/index.tsx +86 -0
- package/src/components/Progress/_.stories.tsx +93 -0
- package/src/components/Progress/_.style.ts +15 -0
- package/src/components/Progress/_.test.tsx +34 -0
- package/src/components/Progress/_.types.ts +17 -0
- package/src/components/Progress/index.tsx +173 -0
- package/src/components/Radio/_.stories.tsx +391 -0
- package/src/components/Radio/_.style.ts +33 -0
- package/src/components/Radio/_.test.tsx +77 -0
- package/src/components/Radio/_.types.ts +14 -0
- package/src/components/Radio/index.tsx +59 -0
- package/src/components/Select/_.stories.tsx +308 -0
- package/src/components/Select/_.style.ts +5 -0
- package/src/components/Select/_.types.ts +24 -0
- package/src/components/Select/index.tsx +172 -0
- package/src/components/Switch/_.stories.tsx +61 -0
- package/src/components/Switch/_.test.tsx +69 -0
- package/src/components/Switch/_.type.ts +12 -0
- package/src/components/Switch/index.tsx +70 -0
- package/src/components/Tabs/_.stories.tsx +508 -0
- package/src/components/Tabs/_.style.ts +63 -0
- package/src/components/Tabs/_.test.tsx +174 -0
- package/src/components/Tabs/_.type.ts +19 -0
- package/src/components/Tabs/index.tsx +35 -0
- package/src/components/Tag/_.stories.tsx +78 -0
- package/src/components/Tag/_.style.ts +71 -0
- package/src/components/Tag/_.test.tsx +44 -0
- package/src/components/Tag/_.types.ts +27 -0
- package/src/components/Tag/index.tsx +46 -0
- package/src/components/TextArea/_.stories.tsx +62 -0
- package/src/components/TextArea/_.style.ts +11 -0
- package/src/components/TextArea/_.test.tsx +43 -0
- package/src/components/TextArea/_.types.ts +29 -0
- package/src/components/TextArea/index.tsx +83 -0
- package/src/components/Toast/_.style.tsx +27 -0
- package/src/components/Toast/_.type.ts +30 -0
- package/src/components/Toast/_.utils.ts +23 -0
- package/src/components/Toast/container.tsx +171 -0
- package/src/components/Toast/index.tsx +29 -0
- package/src/components/Tooltip/_.stories.tsx +106 -0
- package/src/components/Tooltip/_.style.ts +27 -0
- package/src/components/Tooltip/_.test.tsx +54 -0
- package/src/components/Tooltip/_.types.ts +31 -0
- package/src/components/Tooltip/index.tsx +80 -0
- package/src/components/developers/AmirHossein.tsx +149 -0
- package/src/components/developers/Fardin.tsx +130 -0
- package/src/components/developers/Maryam.tsx +260 -0
- package/src/components/developers/Milad.tsx +431 -0
- package/src/components/developers/Rasoul.tsx +198 -0
- package/src/components/index.ts +28 -0
- package/src/components/ui/accordion.tsx +162 -0
- package/src/components/ui/avatars-component/avatar-description.tsx +30 -0
- package/src/components/ui/avatars-component/avatar-groups.tsx +68 -0
- package/src/components/ui/avatars-component/avatar-single.tsx +50 -0
- package/src/components/ui/card.tsx +92 -0
- package/src/components/ui/checkbox-group/plan-card/basic/_.test.tsx +66 -0
- package/src/components/ui/checkbox-group/plan-card/basic/index.tsx +70 -0
- package/src/components/ui/checkbox-group/plan-card/with-header/_.test.tsx +110 -0
- package/src/components/ui/checkbox-group/plan-card/with-header/header.test.tsx +96 -0
- package/src/components/ui/checkbox-group/plan-card/with-header/header.tsx +74 -0
- package/src/components/ui/checkbox-group/plan-card/with-header/index.tsx +65 -0
- package/src/components/ui/file-content/File-content.tsx +43 -0
- package/src/components/ui/file-uploader-components/file-uploader-box.tsx +76 -0
- package/src/components/ui/file-uploader-components/file-uploader-item.tsx +64 -0
- package/src/components/ui/icon-wrapper/_.test.tsx +60 -0
- package/src/components/ui/icon-wrapper/index.tsx +19 -0
- package/src/components/ui/input-component/input-label.tsx +11 -0
- package/src/components/ui/number.tsx +18 -0
- package/src/components/ui/pagination/card-minimal-center-align.tsx +96 -0
- package/src/components/ui/pagination/card-minimal-right-aligne.tsx +90 -0
- package/src/components/ui/pagination/default-pagination.tsx +128 -0
- package/src/components/ui/pagination/get-pagination-item.tsx +36 -0
- package/src/components/ui/pagination/pagination-card-button-group-aligned.tsx +94 -0
- package/src/components/ui/pagination/pagination-card-minimal-left-aligned.tsx +90 -0
- package/src/components/ui/pagination/pagination-content.tsx +15 -0
- package/src/components/ui/pagination/pagination-item.tsx +11 -0
- package/src/components/ui/pagination/pagination-link.tsx +42 -0
- package/src/components/ui/tab-components/tabs-content.tsx +15 -0
- package/src/components/ui/tab-components/tabs-list.tsx +27 -0
- package/src/components/ui/tab-components/tabs-trigger.tsx +25 -0
- package/src/components/ui/text-content-wrapper.tsx +36 -0
- package/src/hooks/useClickOutside.ts +23 -0
- package/src/icons/general/ArrowLeft.tsx +31 -0
- package/src/icons/general/ArrowRight.tsx +31 -0
- package/src/icons/general/activity-heart.tsx +31 -0
- package/src/icons/general/activity.tsx +31 -0
- package/src/icons/general/anchor.tsx +31 -0
- package/src/icons/general/archive.tsx +31 -0
- package/src/icons/general/arrow-left.tsx +25 -0
- package/src/icons/general/arrow-right.tsx +25 -0
- package/src/icons/general/asterisk-01.tsx +31 -0
- package/src/icons/general/asterisk-02.tsx +31 -0
- package/src/icons/general/at-sign.tsx +31 -0
- package/src/icons/general/attention-mark.tsx +43 -0
- package/src/icons/general/bookmark-add.tsx +31 -0
- package/src/icons/general/bookmark.tsx +31 -0
- package/src/icons/general/chevron-left.tsx +25 -0
- package/src/icons/general/chevron-right.tsx +25 -0
- package/src/icons/general/circle-minues.tsx +25 -0
- package/src/icons/general/circle-plus.tsx +25 -0
- package/src/icons/general/circle-question-mark.tsx +32 -0
- package/src/icons/general/circle.tsx +32 -0
- package/src/icons/general/copy.tsx +43 -0
- package/src/icons/general/email.tsx +32 -0
- package/src/icons/general/home.tsx +25 -0
- package/src/icons/general/layer.tsx +36 -0
- package/src/icons/general/leading.tsx +19 -0
- package/src/icons/general/master-card.tsx +37 -0
- package/src/icons/general/minus.tsx +36 -0
- package/src/icons/general/plus.tsx +19 -0
- package/src/icons/general/remove.tsx +32 -0
- package/src/icons/general/slash-divider.tsx +26 -0
- package/src/icons/general/tick-box.tsx +37 -0
- package/src/icons/general/trailing.tsx +19 -0
- package/src/icons/general/unkown-format.tsx +25 -0
- package/src/icons/general/visa-card.tsx +38 -0
- package/src/icons/general/x-close.tsx +35 -0
- package/src/icons/icons.type.ts +7 -0
- package/src/index.css +21 -0
- package/src/index.ts +3 -0
- package/src/lib/utils.ts +6 -0
- package/src/lib/zIndexUtils.ts +2 -0
- package/src/main.tsx +50 -0
- package/src/vite-env.d.ts +1 -0
- package/tests/setup.ts +8 -0
- package/tsconfig.app.json +31 -0
- package/tsconfig.json +7 -0
- package/tsconfig.node.json +24 -0
- package/tsconfig.rollup.json +12 -0
- package/vite.config.ts +20 -0
- package/vitest.config.ts +47 -0
|
@@ -0,0 +1,174 @@
|
|
|
1
|
+
import { render, screen } from "@testing-library/react";
|
|
2
|
+
import { describe, it, expect } from "vitest";
|
|
3
|
+
import { Tabs } from ".";
|
|
4
|
+
|
|
5
|
+
describe("Tabs Component", () => {
|
|
6
|
+
it("renders with default horizontal direction and dir rtl", () => {
|
|
7
|
+
const { container } = render(
|
|
8
|
+
<Tabs className="custom-tabs" defaultValue="tab1">
|
|
9
|
+
<Tabs.Content value="tab1">Content</Tabs.Content>
|
|
10
|
+
</Tabs>
|
|
11
|
+
);
|
|
12
|
+
const tabsRoot = container.querySelector('[data-slot="tabs"]');
|
|
13
|
+
expect(tabsRoot).toBeDefined();
|
|
14
|
+
expect(tabsRoot).toHaveClass("flex-col");
|
|
15
|
+
expect(tabsRoot).toHaveClass("custom-tabs");
|
|
16
|
+
expect(tabsRoot).toHaveAttribute("dir", "rtl");
|
|
17
|
+
});
|
|
18
|
+
|
|
19
|
+
it("renders with vertical direction when direction is 'vertical'", () => {
|
|
20
|
+
const { container } = render(
|
|
21
|
+
<Tabs direction="vertical" defaultValue="tab1">
|
|
22
|
+
<Tabs.Content value="tab1">Content</Tabs.Content>
|
|
23
|
+
</Tabs>
|
|
24
|
+
);
|
|
25
|
+
const tabsRoot = container.querySelector('[data-slot="tabs"]');
|
|
26
|
+
expect(tabsRoot).toBeDefined();
|
|
27
|
+
expect(tabsRoot).toHaveClass("flex-row");
|
|
28
|
+
});
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
describe("Tabs.List Component", () => {
|
|
32
|
+
it("renders and applies variant styles with fullWidth true", () => {
|
|
33
|
+
render(
|
|
34
|
+
<Tabs defaultValue="tab1">
|
|
35
|
+
<Tabs.List fullWidth listVariant="brand" direction="horizontal">
|
|
36
|
+
<div>List Child</div>
|
|
37
|
+
</Tabs.List>
|
|
38
|
+
</Tabs>
|
|
39
|
+
);
|
|
40
|
+
const listChild = screen.getByText("List Child");
|
|
41
|
+
const tabsList = listChild.closest('[data-slot="tabs-list"]');
|
|
42
|
+
expect(tabsList).toBeDefined();
|
|
43
|
+
expect(tabsList).toHaveClass("w-full");
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
it("renders and does not include fullWidth class when fullWidth is false", () => {
|
|
47
|
+
render(
|
|
48
|
+
<Tabs defaultValue="tab1">
|
|
49
|
+
<Tabs.List fullWidth={false} listVariant="outlined" direction="vertical">
|
|
50
|
+
<div>List Child</div>
|
|
51
|
+
</Tabs.List>
|
|
52
|
+
</Tabs>
|
|
53
|
+
);
|
|
54
|
+
const listChild = screen.getByText("List Child");
|
|
55
|
+
const tabsList = listChild.closest('[data-slot="tabs-list"]');
|
|
56
|
+
expect(tabsList).toBeDefined();
|
|
57
|
+
expect(tabsList).not.toHaveClass("w-full");
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
it("merges extra class names provided via className prop", () => {
|
|
61
|
+
render(
|
|
62
|
+
<Tabs defaultValue="tab1">
|
|
63
|
+
<Tabs.List
|
|
64
|
+
className="extra-class"
|
|
65
|
+
fullWidth={true}
|
|
66
|
+
listVariant="outlined"
|
|
67
|
+
direction="horizontal"
|
|
68
|
+
>
|
|
69
|
+
<div>List Child</div>
|
|
70
|
+
</Tabs.List>
|
|
71
|
+
</Tabs>
|
|
72
|
+
);
|
|
73
|
+
const listChild = screen.getByText("List Child");
|
|
74
|
+
const tabsList = listChild.closest('[data-slot="tabs-list"]');
|
|
75
|
+
expect(tabsList).toHaveClass("extra-class");
|
|
76
|
+
});
|
|
77
|
+
});
|
|
78
|
+
|
|
79
|
+
describe("Tabs.Trigger Component", () => {
|
|
80
|
+
it("renders and applies variant styles with fullWidth true", () => {
|
|
81
|
+
render(
|
|
82
|
+
<Tabs defaultValue="tab1">
|
|
83
|
+
<Tabs.List>
|
|
84
|
+
<Tabs.Trigger value="tab1" fullWidth triggerVariant="brand">
|
|
85
|
+
Tab Trigger
|
|
86
|
+
</Tabs.Trigger>
|
|
87
|
+
</Tabs.List>
|
|
88
|
+
</Tabs>
|
|
89
|
+
);
|
|
90
|
+
const trigger = screen.getByText("Tab Trigger");
|
|
91
|
+
expect(trigger).toBeDefined();
|
|
92
|
+
expect(trigger).toHaveClass("w-full");
|
|
93
|
+
});
|
|
94
|
+
|
|
95
|
+
it("renders without fullWidth and merges extra class names", () => {
|
|
96
|
+
render(
|
|
97
|
+
<Tabs defaultValue="tab1">
|
|
98
|
+
<Tabs.List>
|
|
99
|
+
<Tabs.Trigger
|
|
100
|
+
value="tab1"
|
|
101
|
+
fullWidth={false}
|
|
102
|
+
triggerVariant="minimal"
|
|
103
|
+
className="trigger-extra"
|
|
104
|
+
>
|
|
105
|
+
Tab Trigger
|
|
106
|
+
</Tabs.Trigger>
|
|
107
|
+
</Tabs.List>
|
|
108
|
+
</Tabs>
|
|
109
|
+
);
|
|
110
|
+
const trigger = screen.getByText("Tab Trigger");
|
|
111
|
+
expect(trigger).toBeDefined();
|
|
112
|
+
expect(trigger).not.toHaveClass("w-full");
|
|
113
|
+
expect(trigger).toHaveClass("trigger-extra");
|
|
114
|
+
});
|
|
115
|
+
it("applies active data-state to selected tab and includes active variant classes", () => {
|
|
116
|
+
render(
|
|
117
|
+
<Tabs defaultValue="tab1">
|
|
118
|
+
<Tabs.List>
|
|
119
|
+
<Tabs.Trigger value="tab1" triggerVariant="brand">
|
|
120
|
+
Tab One
|
|
121
|
+
</Tabs.Trigger>
|
|
122
|
+
<Tabs.Trigger value="tab2" triggerVariant="brand">
|
|
123
|
+
Tab Two
|
|
124
|
+
</Tabs.Trigger>
|
|
125
|
+
</Tabs.List>
|
|
126
|
+
<Tabs.Content value="tab1">Content One</Tabs.Content>
|
|
127
|
+
<Tabs.Content value="tab2">Content Two</Tabs.Content>
|
|
128
|
+
</Tabs>
|
|
129
|
+
);
|
|
130
|
+
|
|
131
|
+
const tabOne = screen.getByText("Tab One");
|
|
132
|
+
const tabTwo = screen.getByText("Tab Two");
|
|
133
|
+
|
|
134
|
+
expect(tabOne).toHaveAttribute("data-state", "active");
|
|
135
|
+
expect(tabTwo).toHaveAttribute("data-state", "inactive");
|
|
136
|
+
|
|
137
|
+
expect(tabOne.className).toContain(
|
|
138
|
+
"data-[state=active]:bg-rbg-brand-primary-alt"
|
|
139
|
+
);
|
|
140
|
+
expect(tabTwo.className).toContain(
|
|
141
|
+
"data-[state=active]:bg-rbg-brand-primary-alt"
|
|
142
|
+
);
|
|
143
|
+
});
|
|
144
|
+
});
|
|
145
|
+
|
|
146
|
+
describe("Tabs.Content Component", () => {
|
|
147
|
+
it("renders and has default classes", () => {
|
|
148
|
+
render(
|
|
149
|
+
<Tabs defaultValue="tab1">
|
|
150
|
+
<Tabs.Content value="tab1">
|
|
151
|
+
<div>Content Body</div>
|
|
152
|
+
</Tabs.Content>
|
|
153
|
+
</Tabs>
|
|
154
|
+
);
|
|
155
|
+
const contentChild = screen.getByText("Content Body");
|
|
156
|
+
const tabsContent = contentChild.closest('[data-slot="tabs-content"]');
|
|
157
|
+
expect(tabsContent).toBeDefined();
|
|
158
|
+
expect(tabsContent).toHaveClass("flex-1");
|
|
159
|
+
expect(tabsContent).toHaveClass("outline-none");
|
|
160
|
+
});
|
|
161
|
+
|
|
162
|
+
it("merges extra class names provided via className prop", () => {
|
|
163
|
+
render(
|
|
164
|
+
<Tabs defaultValue="tab1">
|
|
165
|
+
<Tabs.Content value="tab1" className="content-extra">
|
|
166
|
+
<div>Content Body</div>
|
|
167
|
+
</Tabs.Content>
|
|
168
|
+
</Tabs>
|
|
169
|
+
);
|
|
170
|
+
const contentChild = screen.getByText("Content Body");
|
|
171
|
+
const tabsContent = contentChild.closest('[data-slot="tabs-content"]');
|
|
172
|
+
expect(tabsContent).toHaveClass("content-extra");
|
|
173
|
+
});
|
|
174
|
+
});
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import * as TabsPrimitive from "@radix-ui/react-tabs";
|
|
2
|
+
|
|
3
|
+
export interface TabsProps
|
|
4
|
+
extends React.ComponentProps<typeof TabsPrimitive.Root> {
|
|
5
|
+
direction?: "vertical" | "horizontal";
|
|
6
|
+
dir?: "rtl" | "ltr";
|
|
7
|
+
}
|
|
8
|
+
export interface TabsListProps
|
|
9
|
+
extends React.ComponentProps<typeof TabsPrimitive.List> {
|
|
10
|
+
fullWidth?: boolean;
|
|
11
|
+
listVariant?: "gray" | "brand" | "outlined" | "bordered" | "minimal";
|
|
12
|
+
direction?: "vertical" | "horizontal";
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export interface TabsTriggerProps
|
|
16
|
+
extends React.ComponentProps<typeof TabsPrimitive.Trigger> {
|
|
17
|
+
triggerVariant?: "gray" | "brand" | "outlined" | "bordered" | "minimal";
|
|
18
|
+
fullWidth?: boolean;
|
|
19
|
+
}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import * as TabsPrimitive from "@radix-ui/react-tabs";
|
|
2
|
+
|
|
3
|
+
import { cn } from "@/lib/utils";
|
|
4
|
+
import { TabsContentComp } from "../ui/tab-components/tabs-content";
|
|
5
|
+
import { TabsListComp } from "../ui/tab-components/tabs-list";
|
|
6
|
+
import { TabsTriggerComp } from "../ui/tab-components/tabs-trigger";
|
|
7
|
+
import { TabsProps } from "./_.type";
|
|
8
|
+
|
|
9
|
+
const TabsRoot = ({
|
|
10
|
+
className,
|
|
11
|
+
direction = "horizontal",
|
|
12
|
+
dir = "rtl",
|
|
13
|
+
...props
|
|
14
|
+
}: Readonly<TabsProps>) => {
|
|
15
|
+
return (
|
|
16
|
+
<TabsPrimitive.Root
|
|
17
|
+
data-slot="tabs"
|
|
18
|
+
dir={dir}
|
|
19
|
+
className={cn(
|
|
20
|
+
"flex gap-2",
|
|
21
|
+
direction == "horizontal" ? "flex-col" : "flex-row",
|
|
22
|
+
className
|
|
23
|
+
)}
|
|
24
|
+
{...props}
|
|
25
|
+
/>
|
|
26
|
+
);
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
const Tabs = Object.assign(TabsRoot, {
|
|
30
|
+
List: TabsListComp,
|
|
31
|
+
Trigger: TabsTriggerComp,
|
|
32
|
+
Content: TabsContentComp,
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
export { Tabs };
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
import type { Meta, StoryObj } from "@storybook/react-vite";
|
|
2
|
+
import { Tag } from "./index"; // or wherever your component is exported
|
|
3
|
+
import type { TagProps } from "./_.types";
|
|
4
|
+
|
|
5
|
+
const meta: Meta<TagProps> = {
|
|
6
|
+
title: "Components/Tag",
|
|
7
|
+
component: Tag,
|
|
8
|
+
tags: ["autodocs"],
|
|
9
|
+
argTypes: {
|
|
10
|
+
size: {
|
|
11
|
+
control: { type: "radio" },
|
|
12
|
+
options: ["sm", "md", "lg"],
|
|
13
|
+
},
|
|
14
|
+
dir: {
|
|
15
|
+
control: { type: "radio" },
|
|
16
|
+
options: ["rtl", "ltr"],
|
|
17
|
+
},
|
|
18
|
+
icon: {
|
|
19
|
+
control: { type: "radio" },
|
|
20
|
+
options: [undefined, "x-close"],
|
|
21
|
+
},
|
|
22
|
+
pin: { control: "boolean" },
|
|
23
|
+
checkbox: { control: "text" },
|
|
24
|
+
number: { control: "number" },
|
|
25
|
+
avatar: { control: "text" },
|
|
26
|
+
},
|
|
27
|
+
args: {
|
|
28
|
+
size: "sm",
|
|
29
|
+
dir: "rtl",
|
|
30
|
+
children: "Tag Label",
|
|
31
|
+
},
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
export default meta;
|
|
35
|
+
|
|
36
|
+
type Story = StoryObj<TagProps>;
|
|
37
|
+
|
|
38
|
+
export const Default: Story = {};
|
|
39
|
+
|
|
40
|
+
export const WithIcon: Story = {
|
|
41
|
+
args: {
|
|
42
|
+
icon: "x-close",
|
|
43
|
+
},
|
|
44
|
+
};
|
|
45
|
+
|
|
46
|
+
export const WithAvatar: Story = {
|
|
47
|
+
args: {
|
|
48
|
+
avatar: "https://i.pravatar.cc/40?img=3",
|
|
49
|
+
},
|
|
50
|
+
};
|
|
51
|
+
|
|
52
|
+
export const WithNumber: Story = {
|
|
53
|
+
args: {
|
|
54
|
+
number: 7,
|
|
55
|
+
},
|
|
56
|
+
};
|
|
57
|
+
|
|
58
|
+
export const WithCheckbox: Story = {
|
|
59
|
+
args: {
|
|
60
|
+
checkbox: "checkbox-id",
|
|
61
|
+
},
|
|
62
|
+
};
|
|
63
|
+
|
|
64
|
+
export const WithPin: Story = {
|
|
65
|
+
args: {
|
|
66
|
+
pin: true,
|
|
67
|
+
},
|
|
68
|
+
};
|
|
69
|
+
|
|
70
|
+
export const AllFeatures: Story = {
|
|
71
|
+
args: {
|
|
72
|
+
pin: true,
|
|
73
|
+
avatar: "https://i.pravatar.cc/40?img=3",
|
|
74
|
+
icon: "x-close",
|
|
75
|
+
number: 99,
|
|
76
|
+
checkbox: "check-all",
|
|
77
|
+
},
|
|
78
|
+
};
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
import { cva } from "class-variance-authority";
|
|
2
|
+
|
|
3
|
+
const baseStyles =
|
|
4
|
+
"inline-flex items-center h-fit min-w-fit bg-white w-fit rounded-md text-rtext-secondary-700 font-medium border border-rborder-primary";
|
|
5
|
+
|
|
6
|
+
const sizeStyle = {
|
|
7
|
+
sm: "px-2 py-[3px] text-xs gap-1",
|
|
8
|
+
md: "px-[9px] py-0.5 text-sm gap-[5px]",
|
|
9
|
+
lg: "px-2.5 py-1 text-sm gap-1.5",
|
|
10
|
+
};
|
|
11
|
+
|
|
12
|
+
export const tagVariants = cva(baseStyles, {
|
|
13
|
+
variants: {
|
|
14
|
+
size: sizeStyle,
|
|
15
|
+
dir: {
|
|
16
|
+
ltr: "flex-row-reverse",
|
|
17
|
+
rtl: "flex-row",
|
|
18
|
+
},
|
|
19
|
+
icon: {
|
|
20
|
+
"x-close": "x-close",
|
|
21
|
+
},
|
|
22
|
+
// pin: {
|
|
23
|
+
// true: "[&>span]:relative [&>span]:before:content-[''] [&>span]:ms-3 [&>span]:before:absolute [&>span]:before:-start-3 [&>span]:before:top-1/2 [&>span]:before:-translate-y-1/2 [&>span]:before:w-1.5 [&>span]:before:h-1.5 [&>span]:before:rounded-full [&>span]:before:bg-success-500 [&>span]:before:me-1 [&>span]:before:ltr:ms-1",
|
|
24
|
+
// },
|
|
25
|
+
},
|
|
26
|
+
defaultVariants: {
|
|
27
|
+
size: "sm",
|
|
28
|
+
dir: "rtl",
|
|
29
|
+
},
|
|
30
|
+
});
|
|
31
|
+
// checkbox variants
|
|
32
|
+
export const checkboxVariants = cva("", {
|
|
33
|
+
variants: {
|
|
34
|
+
size: {
|
|
35
|
+
sm: "w-3.5 h-3.5",
|
|
36
|
+
md: "w-4 h-4",
|
|
37
|
+
lg: "w-4.5 h-4.5",
|
|
38
|
+
},
|
|
39
|
+
},
|
|
40
|
+
defaultVariants: {
|
|
41
|
+
size: "sm",
|
|
42
|
+
},
|
|
43
|
+
});
|
|
44
|
+
// number variant
|
|
45
|
+
export const numberVariants = cva("", {
|
|
46
|
+
variants: {
|
|
47
|
+
size: {
|
|
48
|
+
sm: "px-1 leading-[var(--line-height-text-xs)]",
|
|
49
|
+
md: "px-[5px] leading-[var(--line-height-text-xs)]",
|
|
50
|
+
lg: "px-1.5 leading-[var(--line-height-text-sm)]",
|
|
51
|
+
},
|
|
52
|
+
},
|
|
53
|
+
defaultVariants: {
|
|
54
|
+
size: "sm",
|
|
55
|
+
},
|
|
56
|
+
});
|
|
57
|
+
// remove icon variants
|
|
58
|
+
export const removeIconVariants = cva("", {
|
|
59
|
+
variants: {
|
|
60
|
+
size: {
|
|
61
|
+
sm: "10",
|
|
62
|
+
md: "12",
|
|
63
|
+
lg: "14",
|
|
64
|
+
},
|
|
65
|
+
},
|
|
66
|
+
defaultVariants: {
|
|
67
|
+
size: "sm",
|
|
68
|
+
},
|
|
69
|
+
});
|
|
70
|
+
|
|
71
|
+
export { baseStyles, sizeStyle };
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import { render, screen } from "@testing-library/react";
|
|
2
|
+
import "@testing-library/jest-dom";
|
|
3
|
+
import { describe, expect, it } from "vitest";
|
|
4
|
+
import { Tag } from ".";
|
|
5
|
+
|
|
6
|
+
describe("Tag component", () => {
|
|
7
|
+
it("renders children content", () => {
|
|
8
|
+
render(<Tag>Test Tag</Tag>);
|
|
9
|
+
expect(screen.getByText("Test Tag")).toBeInTheDocument();
|
|
10
|
+
});
|
|
11
|
+
|
|
12
|
+
it("renders checkbox if checkbox prop is passed", () => {
|
|
13
|
+
render(<Tag checkbox="check-1">With Checkbox</Tag>);
|
|
14
|
+
expect(screen.getByRole("checkbox")).toBeInTheDocument();
|
|
15
|
+
});
|
|
16
|
+
|
|
17
|
+
it("renders avatar if avatar prop is passed", () => {
|
|
18
|
+
const testAvatar = "/Avatar.png";
|
|
19
|
+
render(<Tag avatar={testAvatar}>With Avatar</Tag>);
|
|
20
|
+
|
|
21
|
+
const img = screen.getByAltText("avatar") as HTMLImageElement;
|
|
22
|
+
expect(img).toBeInTheDocument();
|
|
23
|
+
expect(img.src).toContain(testAvatar);
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
it("renders number if number prop is passed", () => {
|
|
27
|
+
render(<Tag number={42}>With Number</Tag>);
|
|
28
|
+
expect(screen.getByText("42")).toBeInTheDocument();
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
it("renders icon if icon is 'x-close'", () => {
|
|
32
|
+
render(<Tag icon="x-close">With Icon</Tag>);
|
|
33
|
+
const icon =
|
|
34
|
+
screen.getByRole("x-icon", { hidden: true }) ||
|
|
35
|
+
screen.getByTestId("icon-x-close");
|
|
36
|
+
expect(icon).toBeInTheDocument();
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
it("applies custom className", () => {
|
|
40
|
+
render(<Tag className="custom-class">With Class</Tag>);
|
|
41
|
+
const tagElement = screen.getByText("With Class").parentElement;
|
|
42
|
+
expect(tagElement?.className).toContain("custom-class");
|
|
43
|
+
});
|
|
44
|
+
});
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
export type TagProps = {
|
|
2
|
+
pin?: boolean;
|
|
3
|
+
number?: number;
|
|
4
|
+
checkbox?: string;
|
|
5
|
+
checkboxIndex?: number;
|
|
6
|
+
avatar?: string;
|
|
7
|
+
className?: string;
|
|
8
|
+
size?: "sm" | "md" | "lg";
|
|
9
|
+
dir?: "ltr" | "rtl";
|
|
10
|
+
children?: React.ReactNode;
|
|
11
|
+
icon?: "x-close";
|
|
12
|
+
};
|
|
13
|
+
|
|
14
|
+
export type CheckboxProps = {
|
|
15
|
+
value?: string;
|
|
16
|
+
disabled?: boolean;
|
|
17
|
+
checkboxIndex?: number;
|
|
18
|
+
checked?: boolean;
|
|
19
|
+
defaultChecked?: boolean;
|
|
20
|
+
label?: string;
|
|
21
|
+
id: string;
|
|
22
|
+
name?: string;
|
|
23
|
+
onClick?: () => void;
|
|
24
|
+
onChange?: () => void;
|
|
25
|
+
size?: "sm" | "md" | "lg";
|
|
26
|
+
children?: React.ReactNode;
|
|
27
|
+
};
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import XClose from "@/icons/general/x-close";
|
|
2
|
+
import { cn } from "@/lib/utils";
|
|
3
|
+
import { Checkbox } from "../Checkbox";
|
|
4
|
+
import Number from "../ui/number";
|
|
5
|
+
import { removeIconVariants, tagVariants } from "./_.style";
|
|
6
|
+
import { TagProps } from "./_.types";
|
|
7
|
+
|
|
8
|
+
const Tag = ({
|
|
9
|
+
className,
|
|
10
|
+
size = "sm",
|
|
11
|
+
number,
|
|
12
|
+
pin = false,
|
|
13
|
+
avatar,
|
|
14
|
+
children,
|
|
15
|
+
checkbox,
|
|
16
|
+
dir = "rtl",
|
|
17
|
+
icon,
|
|
18
|
+
checkboxIndex,
|
|
19
|
+
...props
|
|
20
|
+
}: Readonly<TagProps>) => {
|
|
21
|
+
const iconElement = icon === "x-close" && (
|
|
22
|
+
<XClose
|
|
23
|
+
width={cn(removeIconVariants({ size }))}
|
|
24
|
+
height={cn(removeIconVariants({ size }))}
|
|
25
|
+
stroke="#98A2B3"
|
|
26
|
+
/>
|
|
27
|
+
);
|
|
28
|
+
|
|
29
|
+
return (
|
|
30
|
+
<div className={cn(tagVariants({ size, dir, icon }), className)} {...props}>
|
|
31
|
+
{iconElement}
|
|
32
|
+
{number && <Number number={number} size={size} />}
|
|
33
|
+
<span>{children}</span>
|
|
34
|
+
{avatar && (
|
|
35
|
+
<img src={avatar} alt="avatar" className="w-4 h-4 rounded-full" />
|
|
36
|
+
)}
|
|
37
|
+
{pin && <span className="w-1.5 h-1.5 rounded-full bg-current"></span>}
|
|
38
|
+
{checkbox && (
|
|
39
|
+
<Checkbox checkboxIndex={checkboxIndex} size={size} id={checkbox} name={checkbox || ""} />
|
|
40
|
+
)}
|
|
41
|
+
</div>
|
|
42
|
+
);
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
export { Tag };
|
|
46
|
+
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import { textAreaProps } from "@/components/TextArea/_.types";
|
|
2
|
+
import type { StoryObj } from "@storybook/react-vite";
|
|
3
|
+
import { Meta } from "@storybook/react-vite";
|
|
4
|
+
import { TextArea } from "./";
|
|
5
|
+
|
|
6
|
+
export default {
|
|
7
|
+
title: "Components/TextArea",
|
|
8
|
+
component: TextArea,
|
|
9
|
+
tags: ["autodocs"],
|
|
10
|
+
argTypes: {
|
|
11
|
+
disabled: {
|
|
12
|
+
control: { type: "radio" },
|
|
13
|
+
options: [true, false]
|
|
14
|
+
},
|
|
15
|
+
destructive: {
|
|
16
|
+
control: { type: "radio" },
|
|
17
|
+
options: [true, false]
|
|
18
|
+
},
|
|
19
|
+
helpIcon: {
|
|
20
|
+
control: { type: "radio" },
|
|
21
|
+
options: [true, false]
|
|
22
|
+
},
|
|
23
|
+
},
|
|
24
|
+
args: {
|
|
25
|
+
row: 5,
|
|
26
|
+
disabled: false,
|
|
27
|
+
label: "Description",
|
|
28
|
+
required: true,
|
|
29
|
+
hintText: "This is a hint text to help user.",
|
|
30
|
+
placeholder: "Enter a description...",
|
|
31
|
+
destructive: false,
|
|
32
|
+
destructiveText: "This is an error message.",
|
|
33
|
+
helpIcon: true,
|
|
34
|
+
tooltipProps: {
|
|
35
|
+
text: "This is a tooltip",
|
|
36
|
+
description: "Tooltips are used to describe or identify an element. In most scenarios",
|
|
37
|
+
position: "top",
|
|
38
|
+
dir: "ltr",
|
|
39
|
+
descriptionClassName: "max-w-[260px] w-full",
|
|
40
|
+
},
|
|
41
|
+
},
|
|
42
|
+
} as Meta<typeof TextArea>;
|
|
43
|
+
|
|
44
|
+
type Story = StoryObj<textAreaProps>;
|
|
45
|
+
|
|
46
|
+
export const Default: Story = {};
|
|
47
|
+
|
|
48
|
+
export const CustomLabel: Story = {
|
|
49
|
+
args: {
|
|
50
|
+
label: "Email",
|
|
51
|
+
labelClass: "text-blue-600",
|
|
52
|
+
labelStarClass: "text-red-500",
|
|
53
|
+
},
|
|
54
|
+
};
|
|
55
|
+
|
|
56
|
+
export const InputAutoFocus: Story = {
|
|
57
|
+
args: {
|
|
58
|
+
label: "توضیحات",
|
|
59
|
+
placeholder: "توضیحات را وارد کنید",
|
|
60
|
+
autoFocus: true
|
|
61
|
+
},
|
|
62
|
+
};
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import {cva} from "class-variance-authority";
|
|
2
|
+
|
|
3
|
+
const baseStyles =
|
|
4
|
+
"rounded-md p-2 shadow-xs w-fit bg-white border border-rborder-primary" +
|
|
5
|
+
" focus:outline-2 focus:outline-rborder-brand disabled:bg-rbg-disabled-subtle disabled:border-rborder-disabled w-full";
|
|
6
|
+
|
|
7
|
+
const destructiveStyles = "!border-2 !border-rborder-error focus:!outline-0"
|
|
8
|
+
|
|
9
|
+
const textAreaVariants = cva(baseStyles)
|
|
10
|
+
|
|
11
|
+
export {baseStyles,destructiveStyles,textAreaVariants};
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import { fireEvent, render, screen } from "@testing-library/react";
|
|
2
|
+
import { describe, expect, it, vi } from "vitest";
|
|
3
|
+
import { TextArea } from "./index";
|
|
4
|
+
|
|
5
|
+
describe("TextArea component", () => {
|
|
6
|
+
const defaultProps = {
|
|
7
|
+
destructive: false,
|
|
8
|
+
disabled: false,
|
|
9
|
+
destructiveText: "This is an error message.",
|
|
10
|
+
label: "Description",
|
|
11
|
+
helpIcon: true,
|
|
12
|
+
required: true,
|
|
13
|
+
hintText: "This is a hint text to help user.",
|
|
14
|
+
placeholder: "Enter a description...",
|
|
15
|
+
className: "w-fit",
|
|
16
|
+
name:"description"
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
it("calls onChange handler", () => {
|
|
20
|
+
const handleChange = vi.fn();
|
|
21
|
+
render(<TextArea {...defaultProps} onChange={handleChange} />);
|
|
22
|
+
const textarea = screen.getByRole("textbox");
|
|
23
|
+
fireEvent.change(textarea, { target: { value: "Hello world" } });
|
|
24
|
+
expect(handleChange).toHaveBeenCalledTimes(1);
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
it("renders disabled textarea", () => {
|
|
28
|
+
render(<TextArea {...defaultProps} disabled={true} />);
|
|
29
|
+
const textarea = screen.getByRole("textbox");
|
|
30
|
+
expect(textarea).toBeDisabled();
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
it("applies destructive styles when destructive is true", () => {
|
|
34
|
+
render(
|
|
35
|
+
<TextArea
|
|
36
|
+
{...defaultProps}
|
|
37
|
+
destructive={true}
|
|
38
|
+
/>
|
|
39
|
+
);
|
|
40
|
+
const textarea = screen.getByRole("textbox");
|
|
41
|
+
expect(textarea.className).toMatch(/!border-2/);
|
|
42
|
+
});
|
|
43
|
+
});
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
import { Position } from "../Tooltip/_.types";
|
|
3
|
+
|
|
4
|
+
export type textAreaProps = {
|
|
5
|
+
className?: string;
|
|
6
|
+
row?: number;
|
|
7
|
+
disabled?: boolean;
|
|
8
|
+
id?: string;
|
|
9
|
+
value?: string;
|
|
10
|
+
placeholder?: string;
|
|
11
|
+
destructive?: boolean;
|
|
12
|
+
destructiveText?: string;
|
|
13
|
+
label?: string;
|
|
14
|
+
required?: boolean;
|
|
15
|
+
hintText?: string;
|
|
16
|
+
helpIcon?: boolean;
|
|
17
|
+
name: string;
|
|
18
|
+
onChange?: ((e: React.ChangeEvent<HTMLTextAreaElement>) => void) | (() => void);
|
|
19
|
+
tooltipProps?: {
|
|
20
|
+
text: string,
|
|
21
|
+
description?: string,
|
|
22
|
+
position?: Position,
|
|
23
|
+
dir?: "rtl" | "ltr",
|
|
24
|
+
descriptionClassName?: string,
|
|
25
|
+
};
|
|
26
|
+
labelClass?: string;
|
|
27
|
+
labelStarClass?: string;
|
|
28
|
+
autoFocus?: boolean
|
|
29
|
+
}
|