@clickhouse/click-ui 0.0.0 → 0.0.2

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 (236) hide show
  1. package/.eslintrc.cjs +1 -0
  2. package/.prettierrc +14 -0
  3. package/.storybook/main.ts +1 -0
  4. package/.storybook/manager.ts +11 -0
  5. package/.storybook/preview-head.html +2 -4
  6. package/.storybook/preview.tsx +13 -4
  7. package/.storybook/theme.ts +16 -0
  8. package/build-tokens.js +6 -6
  9. package/package.json +25 -5
  10. package/public/favicon.ico +0 -0
  11. package/public/logo.svg +17 -0
  12. package/src/App.tsx +102 -39
  13. package/src/components/Accordion/Accordion.stories.tsx +4 -54
  14. package/src/components/Accordion/Accordion.tsx +1 -0
  15. package/src/components/Alert/Alert.stories.tsx +14 -0
  16. package/src/components/Alert/Alert.test.tsx +27 -0
  17. package/src/components/Alert/Alert.tsx +130 -0
  18. package/src/components/Avatar/Avatar.stories.tsx +13 -0
  19. package/src/components/Avatar/Avatar.tsx +64 -0
  20. package/src/components/Badge/Badge.stories.ts +19 -3
  21. package/src/components/Badge/Badge.test.tsx +6 -1
  22. package/src/components/Badge/Badge.tsx +67 -13
  23. package/src/components/BigStat/BigStat.stories.ts +10 -5
  24. package/src/components/BigStat/BigStat.tsx +36 -24
  25. package/src/components/Button/Button.stories.ts +12 -68
  26. package/src/components/Button/Button.test.tsx +2 -2
  27. package/src/components/Button/Button.tsx +59 -41
  28. package/src/components/ButtonGroup/ButtonGroup.stories.ts +3 -3
  29. package/src/components/Card/Card.stories.ts +3 -3
  30. package/src/components/Checkbox/Checkbox.stories.tsx +35 -0
  31. package/src/components/Checkbox/Checkbox.test.tsx +41 -0
  32. package/src/components/Checkbox/Checkbox.tsx +89 -0
  33. package/src/components/ContextMenu/ContextMenu.stories.tsx +73 -0
  34. package/src/components/ContextMenu/ContextMenu.test.tsx +152 -0
  35. package/src/components/ContextMenu/ContextMenu.tsx +155 -0
  36. package/src/components/Dropdown/Dropdown.stories.tsx +64 -0
  37. package/src/components/Dropdown/Dropdown.test.tsx +141 -0
  38. package/src/components/Dropdown/Dropdown.tsx +149 -0
  39. package/src/components/FormField/Label.stories.tsx +39 -0
  40. package/src/components/FormField/Label.tsx +47 -0
  41. package/src/components/FormField/Select.stories.tsx +48 -0
  42. package/src/components/FormField/Select.test.tsx +216 -0
  43. package/src/components/FormField/Select.tsx +574 -0
  44. package/src/components/FormField/SelectContext.tsx +101 -0
  45. package/src/components/FormField/commonElement.tsx +42 -0
  46. package/src/components/GenericMenu.tsx +114 -0
  47. package/src/components/HoverCard/HoverCard.stories.tsx +64 -0
  48. package/src/components/HoverCard/HoverCard.test.tsx +85 -0
  49. package/src/components/HoverCard/HoverCard.tsx +65 -0
  50. package/src/components/Icon/Icon.stories.ts +4 -35
  51. package/src/components/Icon/Icon.tsx +166 -18
  52. package/src/components/Icon/types.ts +78 -1
  53. package/src/components/IconButton/IconButton.stories.ts +25 -7
  54. package/src/components/IconButton/IconButton.test.tsx +32 -0
  55. package/src/components/IconButton/IconButton.tsx +60 -71
  56. package/src/components/Panel/Panel.stories.tsx +25 -0
  57. package/src/components/Panel/Panel.tsx +33 -0
  58. package/src/components/Popover/Popover.stories.tsx +67 -0
  59. package/src/components/Popover/Popover.test.tsx +46 -0
  60. package/src/components/Popover/Popover.tsx +115 -0
  61. package/src/components/RadioGroup/RadioGroup.stories.tsx +43 -0
  62. package/src/components/RadioGroup/RadioGroup.test.tsx +59 -0
  63. package/src/components/RadioGroup/RadioGroup.tsx +149 -0
  64. package/src/components/Separator/Separator.stories.tsx +24 -0
  65. package/src/components/Separator/Separator.tsx +29 -0
  66. package/src/components/SidebarNavigationItem/SidebarNavigationItem.stories.tsx +2 -2
  67. package/src/components/Spacer/Spacer.stories.tsx +20 -0
  68. package/src/components/Spacer/Spacer.tsx +15 -0
  69. package/src/components/Switch/Switch.stories.ts +3 -3
  70. package/src/components/Switch/Switch.tsx +3 -2
  71. package/src/components/Table/Table.stories.tsx +29 -0
  72. package/src/components/Table/Table.tsx +109 -0
  73. package/src/components/Tabs/Tabs.stories.tsx +2 -37
  74. package/src/components/Tooltip/Tooltip.stories.tsx +68 -0
  75. package/src/components/Tooltip/Tooltip.test.tsx +44 -0
  76. package/src/components/Tooltip/Tooltip.tsx +67 -0
  77. package/src/components/Typography/Text/Text.stories.tsx +22 -0
  78. package/src/components/Typography/Text/Text.test.tsx +16 -0
  79. package/src/components/Typography/Text/Text.tsx +30 -0
  80. package/src/components/Typography/Title/Title.stories.tsx +31 -0
  81. package/src/components/Typography/Title/Title.test.tsx +16 -0
  82. package/src/components/Typography/Title/Title.tsx +36 -0
  83. package/src/components/icons/Activity.tsx +30 -0
  84. package/src/components/icons/ArrowDown.tsx +22 -0
  85. package/src/components/icons/ArrowRight.tsx +22 -0
  86. package/src/components/icons/ArrowTriangle.tsx +36 -0
  87. package/src/components/icons/ArrowUp.tsx +22 -0
  88. package/src/components/icons/Backups.tsx +29 -0
  89. package/src/components/icons/Blog.tsx +38 -0
  90. package/src/components/icons/Book.tsx +30 -0
  91. package/src/components/icons/Brackets.tsx +22 -0
  92. package/src/components/icons/Briefcase.tsx +30 -0
  93. package/src/components/icons/Building.tsx +30 -0
  94. package/src/components/icons/BurgerMenu.tsx +22 -0
  95. package/src/components/icons/Cards.tsx +30 -0
  96. package/src/components/icons/CellTower.tsx +21 -0
  97. package/src/components/icons/CheckIcon.tsx +21 -0
  98. package/src/components/icons/CheckInCircle.tsx +39 -0
  99. package/src/components/icons/ChevronDown.tsx +19 -5
  100. package/src/components/icons/ChevronLeft.tsx +22 -0
  101. package/src/components/icons/ChevronRight.tsx +3 -3
  102. package/src/components/icons/ChevronUp.tsx +22 -0
  103. package/src/components/icons/Clock.tsx +37 -0
  104. package/src/components/icons/Cloud.tsx +23 -0
  105. package/src/components/icons/Code.tsx +22 -0
  106. package/src/components/icons/CodeInSquare.tsx +30 -0
  107. package/src/components/icons/Connect.tsx +22 -0
  108. package/src/components/icons/ConnectAlt.tsx +30 -0
  109. package/src/components/icons/Console.tsx +30 -0
  110. package/src/components/icons/Copy.tsx +33 -0
  111. package/src/components/icons/CrossIcon.tsx +29 -0
  112. package/src/components/icons/Data.tsx +36 -0
  113. package/src/components/icons/DatabaseIcon.tsx +27 -29
  114. package/src/components/icons/Disk.tsx +30 -0
  115. package/src/components/icons/Display.tsx +30 -0
  116. package/src/components/icons/Document.tsx +30 -0
  117. package/src/components/icons/DotsHorizontal.tsx +36 -0
  118. package/src/components/icons/DotsVertical.tsx +33 -0
  119. package/src/components/icons/Email.tsx +33 -0
  120. package/src/components/icons/Empty.tsx +14 -0
  121. package/src/components/icons/FilterIcon.tsx +29 -16
  122. package/src/components/icons/Fire.tsx +23 -0
  123. package/src/components/icons/Folder.tsx +20 -0
  124. package/src/components/icons/Gift.tsx +21 -0
  125. package/src/components/icons/HistoryIcon.tsx +13 -13
  126. package/src/components/icons/Home.tsx +29 -0
  127. package/src/components/icons/Http.tsx +22 -0
  128. package/src/components/icons/Icons.mdx +31 -28
  129. package/src/components/icons/InfoInCircleIcon.tsx +37 -0
  130. package/src/components/icons/InformationIcon.tsx +34 -0
  131. package/src/components/icons/InsertRowIcon.tsx +30 -32
  132. package/src/components/icons/Integrations.tsx +29 -0
  133. package/src/components/icons/LightBulb.tsx +40 -0
  134. package/src/components/icons/Lightening.tsx +30 -0
  135. package/src/components/icons/Loading.tsx +57 -0
  136. package/src/components/icons/Metrics.tsx +38 -0
  137. package/src/components/icons/MetricsAlt.tsx +30 -0
  138. package/src/components/icons/Payment.tsx +23 -0
  139. package/src/components/icons/Payments/Amex.tsx +44 -0
  140. package/src/components/icons/Payments/MasterCard.tsx +48 -0
  141. package/src/components/icons/Payments/Paypal.tsx +41 -0
  142. package/src/components/icons/Payments/Visa.tsx +36 -0
  143. package/src/components/icons/Payments/index.tsx +30 -0
  144. package/src/components/icons/Pencil.tsx +30 -0
  145. package/src/components/icons/PieChart.tsx +30 -0
  146. package/src/components/icons/Play.tsx +30 -0
  147. package/src/components/icons/Plus.tsx +22 -0
  148. package/src/components/icons/Popout.tsx +22 -0
  149. package/src/components/icons/PopoverArrow.tsx +22 -0
  150. package/src/components/icons/Question.tsx +30 -0
  151. package/src/components/icons/Refresh.tsx +29 -0
  152. package/src/components/icons/Search.tsx +22 -0
  153. package/src/components/icons/Secure.tsx +30 -0
  154. package/src/components/icons/Services.tsx +23 -0
  155. package/src/components/icons/Settings.tsx +22 -0
  156. package/src/components/icons/Share.tsx +29 -0
  157. package/src/components/icons/SlideIn.tsx +28 -0
  158. package/src/components/icons/SlideOut.tsx +28 -0
  159. package/src/components/icons/SortAltIcon.tsx +18 -20
  160. package/src/components/icons/SortIcon.tsx +24 -0
  161. package/src/components/icons/Sparkle.tsx +23 -0
  162. package/src/components/icons/Speaker.tsx +30 -0
  163. package/src/components/icons/Speed.tsx +29 -0
  164. package/src/components/icons/Star.tsx +23 -0
  165. package/src/components/icons/Support.tsx +37 -0
  166. package/src/components/icons/Table.tsx +30 -0
  167. package/src/components/icons/Taxi.tsx +120 -0
  168. package/src/components/icons/Trash.tsx +22 -0
  169. package/src/components/icons/Upload.tsx +29 -0
  170. package/src/components/icons/Url.tsx +22 -0
  171. package/src/components/icons/UsersIcon.tsx +27 -27
  172. package/src/components/icons/WarningIcon.tsx +30 -0
  173. package/src/components/index.ts +31 -10
  174. package/src/index.ts +2 -2
  175. package/src/styles/types.ts +715 -295
  176. package/src/styles/variables.classic.json +171 -0
  177. package/src/styles/variables.dark.json +447 -129
  178. package/src/styles/variables.json +830 -410
  179. package/src/styles/variables.light.json +339 -179
  180. package/tokens/themes/$themes.json +3657 -1
  181. package/tokens/themes/classic.json +492 -0
  182. package/tokens/themes/component.json +1126 -441
  183. package/tokens/themes/dark.json +1871 -758
  184. package/tokens/themes/light.json +852 -266
  185. package/tokens/themes/primitives.json +294 -210
  186. package/vite.config.ts +6 -4
  187. package/app/.babelrc +0 -27
  188. package/app/.eslintrc.json +0 -6
  189. package/app/.storybook/main.ts +0 -17
  190. package/app/.storybook/preview.tsx +0 -26
  191. package/app/README.md +0 -38
  192. package/app/next.config.js +0 -6
  193. package/app/package-lock.json +0 -28711
  194. package/app/package.json +0 -44
  195. package/app/public/favicon.ico +0 -0
  196. package/app/public/next.svg +0 -1
  197. package/app/public/vercel.svg +0 -1
  198. package/app/src/assets/RightArrow/right-arrow.tsx +0 -17
  199. package/app/src/assets/S3Logo/s3-logo.tsx +0 -31
  200. package/app/src/assets/amazon_s3.svg +0 -9
  201. package/app/src/assets/arrow.svg +0 -3
  202. package/app/src/globals.d.ts +0 -4
  203. package/app/src/pages/_app.tsx +0 -8
  204. package/app/src/pages/_document.tsx +0 -17
  205. package/app/src/pages/api/hello.ts +0 -13
  206. package/app/src/pages/index.tsx +0 -141
  207. package/app/src/pages/label.tsx +0 -27
  208. package/app/src/stories/assets/code-brackets.svg +0 -1
  209. package/app/src/stories/assets/colors.svg +0 -1
  210. package/app/src/stories/assets/comments.svg +0 -1
  211. package/app/src/stories/assets/direction.svg +0 -1
  212. package/app/src/stories/assets/flow.svg +0 -1
  213. package/app/src/stories/assets/plugin.svg +0 -1
  214. package/app/src/stories/assets/repo.svg +0 -1
  215. package/app/src/stories/assets/stackalt.svg +0 -1
  216. package/app/src/styles/Home.module.css +0 -235
  217. package/app/src/styles/globals.css +0 -111
  218. package/app/src/styles/types.ts +0 -1031
  219. package/app/src/styles/variables.classic.css +0 -16
  220. package/app/src/styles/variables.classic.json +0 -31
  221. package/app/src/styles/variables.css +0 -763
  222. package/app/src/styles/variables.dark.css +0 -135
  223. package/app/src/styles/variables.dark.json +0 -339
  224. package/app/src/styles/variables.json +0 -1029
  225. package/app/src/styles/variables.light.css +0 -203
  226. package/app/src/styles/variables.light.json +0 -478
  227. package/app/tokens/themes/$metadata.json +0 -9
  228. package/app/tokens/themes/$themes.json +0 -1
  229. package/app/tokens/themes/classic.json +0 -58
  230. package/app/tokens/themes/component.json +0 -868
  231. package/app/tokens/themes/dark.json +0 -937
  232. package/app/tokens/themes/light.json +0 -1380
  233. package/app/tokens/themes/primitives.json +0 -859
  234. package/app/tsconfig.json +0 -23
  235. package/src/components/FormField/FormField.stories.ts +0 -14
  236. package/src/components/FormField/FormField.tsx +0 -22
@@ -2,10 +2,12 @@ import { Icon } from "@/components";
2
2
  import { IconName } from "@/components/Icon/types";
3
3
  import styled from "styled-components";
4
4
 
5
- type ButtonType = "primary" | "secondary";
6
- type Alignment = "left";
5
+ type ButtonType = "primary" | "secondary" | "danger";
6
+ type Alignment = "center" | "left";
7
+
7
8
  export interface ButtonProps extends React.HTMLAttributes<HTMLButtonElement> {
8
9
  type?: ButtonType;
10
+ disabled?: boolean;
9
11
  label?: string;
10
12
  iconLeft?: IconName;
11
13
  iconRight?: IconName;
@@ -16,6 +18,7 @@ export interface ButtonProps extends React.HTMLAttributes<HTMLButtonElement> {
16
18
 
17
19
  export interface StyledButtonProps {
18
20
  styleType: ButtonType;
21
+ disabled?: boolean;
19
22
  align?: Alignment;
20
23
  width?: string;
21
24
  height?: string;
@@ -26,14 +29,28 @@ export const Button = ({
26
29
  iconLeft,
27
30
  iconRight,
28
31
  label,
29
- align,
32
+ align = "center",
30
33
  children,
31
34
  ...delegated
32
35
  }: ButtonProps) => (
33
- <StyledButton styleType={type} align={align} {...delegated}>
34
- {iconLeft && <Icon name={iconLeft} size="small" />}
36
+ <StyledButton
37
+ styleType={type}
38
+ align={align}
39
+ {...delegated}
40
+ >
41
+ {iconLeft && (
42
+ <Icon
43
+ name={iconLeft}
44
+ size="small"
45
+ />
46
+ )}
35
47
  {label ? label : children}
36
- {iconRight && <Icon name={iconRight} size="small" />}
48
+ {iconRight && (
49
+ <Icon
50
+ name={iconRight}
51
+ size="small"
52
+ />
53
+ )}
37
54
  </StyledButton>
38
55
  );
39
56
 
@@ -41,57 +58,58 @@ const BaseButton = styled.button`
41
58
  display: flex;
42
59
  flex-direction: row;
43
60
  justify-content: center;
44
- align-items: center;
45
-
46
- padding-left: ${props => props.theme.click.button.basic.space.x};
47
- padding-right: ${props => props.theme.click.button.basic.space.x};
48
- padding-top: ${props => props.theme.click.button.basic.space.y};
49
- padding-bottom: ${props => props.theme.click.button.basic.space.y};
61
+ padding: ${props =>
62
+ `${props.theme.click.button.basic.space.y} ${props.theme.click.button.basic.space.x}`};
50
63
  border-radius: ${props => props.theme.click.button.radii.all};
51
64
  gap: ${props => props.theme.click.button.basic.space.gap};
52
65
  cursor: pointer;
53
66
  `;
54
67
 
55
- const StyledButton = styled(BaseButton)<StyledButtonProps>`
68
+ const StyledButton = styled(BaseButton)<
69
+ Pick<StyledButtonProps, "width" | "height" | "align" | "styleType">
70
+ >`
56
71
  width: ${props => props.width};
57
72
  height: ${props => props.height};
73
+ font: ${props => props.theme.click.button.basic.typography.label.default};
74
+ color: ${({ styleType = "primary", theme }) =>
75
+ theme.click.button.basic.color[styleType].text.default};
76
+ background-color: ${({ styleType = "primary", theme }) =>
77
+ theme.click.button.basic.color[styleType].background.default};
78
+ border: 1px solid
79
+ ${({ styleType = "primary", theme }) =>
80
+ theme.click.button.basic.color[styleType].stroke.default};
58
81
 
59
82
  display: flex;
60
83
  align-items: center;
61
- justify-content: ${props =>
62
- props.align === "left" ? "flex-start" : "center"};
84
+ justify-content: ${props => (props.align === "left" ? "flex-start" : "center")};
63
85
 
64
- border: 1px solid
65
- ${props =>
66
- props.theme.click.button.basic.color[props.styleType].stroke.default};
67
- background-color: ${props =>
68
- props.theme.click.button.basic.color[props.styleType].background.default};
69
- color: ${props =>
70
- props.theme.click.button.basic.color[props.styleType].text.default};
71
- font: ${props => props.theme.click.button.basic.typography.label.default};
72
-
73
- &:active {
86
+ &:hover {
87
+ background-color: ${({ styleType = "primary", theme }) =>
88
+ theme.click.button.basic.color[styleType].background.hover};
74
89
  border: 1px solid
75
- ${props =>
76
- props.theme.click.button.basic.color[props.styleType].stroke.active};
77
- background-color: ${props =>
78
- props.theme.click.button.basic.color[props.styleType].background.active};
79
- font: ${props => props.theme.click.button.basic.typography.label.active};
90
+ ${({ styleType = "primary", theme }) =>
91
+ theme.click.button.basic.color[styleType].stroke.hover};
80
92
  }
81
93
 
82
- &:hover {
94
+ &:active,
95
+ &:focus {
96
+ background-color: ${({ styleType = "primary", theme }) =>
97
+ theme.click.button.basic.color[styleType].background.active};
83
98
  border: 1px solid
84
- ${props =>
85
- props.theme.click.button.basic.color[props.styleType].stroke.hover};
86
- background-color: ${props =>
87
- props.theme.click.button.basic.color[props.styleType].background.hover};
88
- font: ${props => props.theme.click.button.basic.typography.label.hover};
99
+ ${({ styleType = "primary", theme }) =>
100
+ theme.click.button.basic.color[styleType].stroke.active};
89
101
  }
90
102
 
91
- &[disabled] {
92
- background-color: ${props =>
93
- props.theme.click.button.basic.color.disabled.background.default};
94
- color: ${props =>
95
- props.theme.click.button.basic.color.disabled.text.default};
103
+ &:disabled,
104
+ &:disabled:hover,
105
+ &:disabled:active {
106
+ background-color: ${({ styleType = "primary", theme }) =>
107
+ theme.click.button.basic.color[styleType].background.disabled};
108
+ color: ${({ styleType = "primary", theme }) =>
109
+ theme.click.button.basic.color[styleType].text.disabled};
110
+ border: 1px solid
111
+ ${({ styleType = "primary", theme }) =>
112
+ theme.click.button.basic.color[styleType].stroke.disabled};
113
+ cursor: not-allowed;
96
114
  }
97
115
  `;
@@ -2,11 +2,11 @@ import { ButtonGroup } from "./ButtonGroup";
2
2
 
3
3
  export default {
4
4
  component: ButtonGroup,
5
- title: "ButtonGroup",
6
- tags: ["buttons"],
5
+ title: "Buttons/ButtonGroup",
6
+ tags: ["button-group", "autodocs"],
7
7
  };
8
8
 
9
- export const Default = {
9
+ export const Playground = {
10
10
  args: {
11
11
  labels: ["Button 1", "Button 2", "Button 3"],
12
12
  activeIndex: 2,
@@ -2,11 +2,11 @@ import { Card } from "./Card";
2
2
 
3
3
  export default {
4
4
  component: Card,
5
- title: "Card",
6
- tags: ["card"],
5
+ title: "Display/Card",
6
+ tags: ["card","autodocs"],
7
7
  };
8
8
 
9
- export const Default = {
9
+ export const Playground = {
10
10
  args: {
11
11
  title: "Card title",
12
12
  badgeText: "experiment",
@@ -0,0 +1,35 @@
1
+ import { Checkbox } from "./Checkbox";
2
+
3
+ const CheckboxComponent = ({
4
+ checked,
5
+ ...props
6
+ }: {
7
+ checked: "default" | "checked" | "unchecked";
8
+ disabled: boolean;
9
+ label?: string;
10
+ }) => {
11
+ return (
12
+ <Checkbox
13
+ checked={checked === "default" ? undefined : checked === "checked"}
14
+ {...props}
15
+ />
16
+ );
17
+ };
18
+ export default {
19
+ component: CheckboxComponent,
20
+ title: "Forms/Checkbox",
21
+ tags: ["checkbox","autodocs"],
22
+ argTypes: {
23
+ checked: { control: "radio", options: ["default", "checked", "unchecked"] },
24
+ disabled: { control: "boolean" },
25
+ label: { control: "text" },
26
+ },
27
+ };
28
+
29
+ export const Playground = {
30
+ args: {
31
+ label: "Accept terms and conditions",
32
+ disabled: false,
33
+ checked: "default",
34
+ },
35
+ };
@@ -0,0 +1,41 @@
1
+ import { ThemeProvider } from "styled-components";
2
+ import { themes } from "../../theme";
3
+ import { render, fireEvent } from "@testing-library/react";
4
+ import { Checkbox } from "@/components";
5
+ import { CheckboxProps } from "@/components/Checkbox/Checkbox";
6
+
7
+ describe("Checkbox", () => {
8
+ const renderCheckbox = (props: CheckboxProps) =>
9
+ render(
10
+ <ThemeProvider theme={themes.dark}>
11
+ <Checkbox {...props} />
12
+ </ThemeProvider>
13
+ );
14
+
15
+ it("should execute action on click", () => {
16
+ let counter = 0;
17
+ const handleClick = () => counter++;
18
+ const { getByTestId } = renderCheckbox({
19
+ onCheckedChange: handleClick,
20
+ label: "Accept terms and conditions",
21
+ });
22
+ const checkbox = getByTestId("checkbox");
23
+ fireEvent.click(checkbox);
24
+
25
+ expect(counter).toEqual(1);
26
+ });
27
+
28
+ it("should not execute action on click if the checkbox is disabled", () => {
29
+ let counter = 0;
30
+ const handleClick = () => counter++;
31
+ const { getByTestId } = renderCheckbox({
32
+ onCheckedChange: handleClick,
33
+ label: "Accept terms and conditions",
34
+ disabled: true,
35
+ });
36
+ const checkbox = getByTestId("checkbox");
37
+ fireEvent.click(checkbox);
38
+
39
+ expect(counter).toEqual(0);
40
+ });
41
+ });
@@ -0,0 +1,89 @@
1
+ import { Icon } from "@/components";
2
+ import * as RadixCheckbox from "@radix-ui/react-checkbox";
3
+ import { useId } from "react";
4
+ import styled from "styled-components";
5
+
6
+ export interface CheckboxProps extends RadixCheckbox.CheckboxProps {
7
+ label?: string;
8
+ }
9
+ export const Checkbox = ({ id, label = "", ...delegated }: CheckboxProps) => {
10
+ const defaultId = useId();
11
+ return (
12
+ <Wrapper>
13
+ <CheckInput
14
+ id={id ?? defaultId}
15
+ data-testid="checkbox"
16
+ {...delegated}
17
+ >
18
+ <CheckIconWrapper>
19
+ <Icon
20
+ name="check"
21
+ size="small"
22
+ />
23
+ </CheckIconWrapper>
24
+ </CheckInput>
25
+ {label && <label htmlFor={id ?? defaultId}>{label}</label>}
26
+ </Wrapper>
27
+ );
28
+ };
29
+
30
+ const Wrapper = styled.div`
31
+ padding: ${({ theme }) => theme.click.checkbox.space.all};
32
+ display: flex;
33
+ align-items: center;
34
+ gap: ${({ theme }) => theme.click.checkbox.space.gap};
35
+ `;
36
+
37
+ const CheckInput = styled(RadixCheckbox.Root)`
38
+ display: flex;
39
+ align-items: center;
40
+ justify-content: center;
41
+
42
+ ${({ theme }) => `
43
+ border-radius: ${theme.click.checkbox.radii.all};
44
+ width: ${theme.click.checkbox.size.all};
45
+ height: ${theme.click.checkbox.size.all};
46
+ background: ${theme.click.checkbox.color.background.default};
47
+ border: 1px solid ${theme.click.checkbox.color.stroke.default};
48
+ cursor: pointer;
49
+
50
+ & ~ label {
51
+ color: ${theme.click.checkbox.color.label.default};
52
+ font: ${theme.click.field.typography.fieldText.default}
53
+ cursor: pointer;
54
+ }
55
+ &:hover {
56
+ background: ${theme.click.checkbox.color.background.hover};
57
+ &~ label {
58
+ color: ${theme.click.checkbox.color.label.hover};
59
+ }
60
+ }
61
+ &[data-state="checked"] {
62
+ border-color: ${theme.click.checkbox.color.stroke.active};
63
+ background: ${theme.click.checkbox.color.background.active};
64
+ & ~ label {
65
+ color: ${theme.click.checkbox.color.label.active};
66
+ }
67
+ }
68
+ &[data-disabled] {
69
+ background: ${theme.click.checkbox.color.background.disabled};
70
+ border-color: ${theme.click.checkbox.color.stroke.disabled};
71
+ &[data-state="checked"] {
72
+ background: ${theme.click.checkbox.color.background.disabled};
73
+ border-color: ${theme.click.checkbox.color.stroke.disabled};
74
+ }
75
+ & ~ label {
76
+ color: ${theme.click.checkbox.color.label.disabled};
77
+ }
78
+ }
79
+ `};
80
+ `;
81
+
82
+ const CheckIconWrapper = styled(RadixCheckbox.Indicator)`
83
+ ${({ theme }) => `
84
+ color: ${theme.click.checkbox.color.check.active};
85
+ &[data-disabled] {
86
+ color: ${theme.click.checkbox.color.check.disabled};
87
+ }
88
+ `}
89
+ `;
@@ -0,0 +1,73 @@
1
+ import { ContextMenuProps } from "@radix-ui/react-context-menu";
2
+ import { ContextMenu } from "./ContextMenu";
3
+ import styled from "styled-components";
4
+
5
+ interface Props extends ContextMenuProps {
6
+ disabled?: boolean;
7
+ showArrow?: boolean;
8
+ side: "top" | "right" | "left" | "bottom";
9
+ }
10
+ const GridCenter = styled.div`
11
+ display: grid;
12
+ place-items: center;
13
+ width: 100%;
14
+ height: 100%;
15
+ `;
16
+
17
+ const Trigger = styled(GridCenter)`
18
+ border: 2px currentColor dashed;
19
+ `;
20
+ const ContextMenuExample = ({ showArrow, disabled, side, ...props }: Props) => {
21
+ return (
22
+ <GridCenter>
23
+ <ContextMenu {...props}>
24
+ <ContextMenu.Trigger disabled={disabled}>
25
+ <Trigger>ContextMenu Trigger</Trigger>
26
+ </ContextMenu.Trigger>
27
+ <ContextMenu.Content
28
+ showArrow={showArrow}
29
+ side={side}
30
+ >
31
+ <ContextMenu.Group>
32
+ <ContextMenu.Item>Content0</ContextMenu.Item>
33
+ </ContextMenu.Group>
34
+ <ContextMenu.Item>Content1 long text content</ContextMenu.Item>
35
+ <ContextMenu.Sub>
36
+ <ContextMenu.SubTrigger>Hover over</ContextMenu.SubTrigger>
37
+ <ContextMenu.Content sub>
38
+ <ContextMenu.Item>SubContent0</ContextMenu.Item>
39
+ <ContextMenu.Item>SubContent1</ContextMenu.Item>
40
+ <ContextMenu.Item>SubContent0</ContextMenu.Item>
41
+ <ContextMenu.Item>SubContent1</ContextMenu.Item>
42
+ <ContextMenu.Item>SubContent0</ContextMenu.Item>
43
+ <ContextMenu.Item>SubContent1</ContextMenu.Item>
44
+ <ContextMenu.Item>SubContent0</ContextMenu.Item>
45
+ <ContextMenu.Item>SubContent1</ContextMenu.Item>
46
+ <ContextMenu.Item>SubContent0</ContextMenu.Item>
47
+ <ContextMenu.Item>SubContent1</ContextMenu.Item>
48
+ </ContextMenu.Content>
49
+ </ContextMenu.Sub>
50
+ <ContextMenu.Item>Content2</ContextMenu.Item>
51
+ <ContextMenu.Item disabled>Content3</ContextMenu.Item>
52
+ </ContextMenu.Content>
53
+ </ContextMenu>
54
+ </GridCenter>
55
+ );
56
+ };
57
+ export default {
58
+ component: ContextMenuExample,
59
+ title: "Display/ContextMenu",
60
+ tags: ["form-field", "dropdown", "autodocs"],
61
+ argTypes: {
62
+ disabled: { control: "boolean" },
63
+ showArrow: { control: "boolean" },
64
+ side: { control: "select", options: ["top", "right", "left", "bottom"] },
65
+ },
66
+ };
67
+
68
+ export const Playground = {
69
+ args: {
70
+ showArrow: true,
71
+ side: "left",
72
+ },
73
+ };
@@ -0,0 +1,152 @@
1
+ import { ThemeProvider } from "styled-components";
2
+ import { render, fireEvent, waitFor } from "@testing-library/react";
3
+ import { ContextMenuProps } from "@radix-ui/react-context-menu";
4
+ import userEvent from "@testing-library/user-event";
5
+ import { themes } from "../../theme";
6
+ import { ContextMenu } from "./ContextMenu";
7
+
8
+ interface Props extends ContextMenuProps {
9
+ disabled?: boolean;
10
+ }
11
+
12
+ describe("ContextMenu", () => {
13
+ beforeAll(() => {
14
+ window.HTMLElement.prototype.scrollIntoView = jest.fn();
15
+ global.ResizeObserver = jest.fn().mockImplementation(() => ({
16
+ observe: jest.fn(),
17
+ unobserve: jest.fn(),
18
+ disconnect: jest.fn(),
19
+ }));
20
+ global.DOMRect = class DOMRect {
21
+ bottom = 0;
22
+ left = 0;
23
+ right = 0;
24
+ top = 0;
25
+ constructor(public x = 0, public y = 0, public width = 0, public height = 0) {}
26
+ static fromRect(other?: DOMRectInit): DOMRect {
27
+ return new DOMRect(other?.x, other?.y, other?.width, other?.height);
28
+ }
29
+ toJSON() {
30
+ return JSON.stringify(this);
31
+ }
32
+ };
33
+ });
34
+ const renderContextMenu = ({ disabled, ...props }: Props) =>
35
+ render(
36
+ <ThemeProvider theme={themes.dark}>
37
+ <ContextMenu {...props}>
38
+ <ContextMenu.Trigger disabled={disabled}>
39
+ <div>ContextMenu Trigger</div>
40
+ </ContextMenu.Trigger>
41
+ <ContextMenu.Content>
42
+ <ContextMenu.Group>
43
+ <ContextMenu.Item>Content0</ContextMenu.Item>
44
+ </ContextMenu.Group>
45
+ <ContextMenu.Item>Content1 long text content</ContextMenu.Item>
46
+ <ContextMenu.Sub>
47
+ <ContextMenu.SubTrigger>Hover over</ContextMenu.SubTrigger>
48
+ <ContextMenu.Content sub>
49
+ <ContextMenu.Item>SubContent0</ContextMenu.Item>
50
+ <ContextMenu.Item>SubContent1</ContextMenu.Item>
51
+ </ContextMenu.Content>
52
+ </ContextMenu.Sub>
53
+ <ContextMenu.Item>Content2</ContextMenu.Item>
54
+ <ContextMenu.Item disabled>Content3</ContextMenu.Item>
55
+ </ContextMenu.Content>
56
+ </ContextMenu>
57
+ </ThemeProvider>
58
+ );
59
+
60
+ it("should open menu on rightclick", () => {
61
+ const { getByText } = renderContextMenu({});
62
+ const contextMenuTrigger = getByText("ContextMenu Trigger");
63
+ expect(contextMenuTrigger).not.toBeNull();
64
+ fireEvent.contextMenu(contextMenuTrigger);
65
+ expect(getByText("Content0")).not.toBeNull();
66
+ });
67
+
68
+ it("should not open disabled contextMenu on pointer", () => {
69
+ const { getByText, queryByText } = renderContextMenu({
70
+ disabled: true,
71
+ });
72
+ const contextMenuTrigger = getByText("ContextMenu Trigger");
73
+ fireEvent.contextMenu(contextMenuTrigger);
74
+ expect(queryByText("Content0")).toBeNull();
75
+ });
76
+
77
+ it("should close contextMenu on clicking outside content", async () => {
78
+ const { getByText, queryByText } = renderContextMenu({});
79
+ const contextMenuTrigger = getByText("ContextMenu Trigger");
80
+ fireEvent.contextMenu(contextMenuTrigger);
81
+ expect(queryByText("Content0")).not.toBeNull();
82
+ fireEvent.click(document, {
83
+ ctrlKey: false,
84
+ button: 0,
85
+ });
86
+ await waitFor(() => {
87
+ expect(queryByText("Content1")).toBeNull();
88
+ });
89
+ });
90
+
91
+ it("should close contextMenu on selecting item", () => {
92
+ const { getByText, queryByText } = renderContextMenu({});
93
+ const contextMenuTrigger = getByText("ContextMenu Trigger");
94
+ expect(contextMenuTrigger).not.toBeNull();
95
+ fireEvent.contextMenu(contextMenuTrigger);
96
+ expect(getByText("Content0")).not.toBeNull();
97
+ const item = queryByText("Content0");
98
+ expect(item).not.toBeNull();
99
+ item && fireEvent.click(item);
100
+ expect(item).not.toBeNull();
101
+ expect(queryByText("Content1")).toBeNull();
102
+ });
103
+
104
+ it("should open submenu contextMenu on selecting item with subcontent", async () => {
105
+ const { getByText, queryByText } = renderContextMenu({});
106
+ const contextMenuTrigger = getByText("ContextMenu Trigger");
107
+ expect(contextMenuTrigger).not.toBeNull();
108
+ fireEvent.contextMenu(contextMenuTrigger);
109
+
110
+ expect(queryByText("Content0")).not.toBeNull();
111
+ const item = getByText("Hover over");
112
+ expect(item).not.toBeNull();
113
+ await userEvent.hover(item);
114
+ await waitFor(() => {
115
+ expect(queryByText("SubContent0")).not.toBeNull();
116
+ });
117
+ expect(item).not.toBeNull();
118
+ });
119
+
120
+ it("should close contextMenu on selecting sub item", async () => {
121
+ const { getByText, queryByText } = renderContextMenu({});
122
+ const contextMenuTrigger = getByText("ContextMenu Trigger");
123
+ fireEvent.contextMenu(contextMenuTrigger);
124
+
125
+ expect(queryByText("Content0")).not.toBeNull();
126
+ const item = queryByText("Hover over");
127
+ item && (await userEvent.hover(item));
128
+ await waitFor(() => {
129
+ expect(queryByText("SubContent0")).not.toBeNull();
130
+ });
131
+ expect(item).not.toBeNull();
132
+ const subItem = queryByText("SubContent0");
133
+ subItem && fireEvent.click(subItem);
134
+ await waitFor(() => {
135
+ expect(queryByText("SubContent1")).toBeNull();
136
+ });
137
+ expect(queryByText("Content0")).toBeNull();
138
+ });
139
+
140
+ it("should not close contextMenu on selecting disabled item", () => {
141
+ const { getByText, queryByText } = renderContextMenu({});
142
+ const contextMenuTrigger = getByText("ContextMenu Trigger");
143
+ fireEvent.contextMenu(contextMenuTrigger);
144
+
145
+ expect(queryByText("Content3")).not.toBeNull();
146
+ const item = queryByText("Content3");
147
+ expect(item).not.toBeNull();
148
+ item && fireEvent.pointerDown(item);
149
+ expect(item).not.toBeNull();
150
+ expect(queryByText("Content2")).not.toBeNull();
151
+ });
152
+ });