@livechat/design-system-react-components 1.0.0-alpha.2 → 1.0.0-alpha.4

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 (271) hide show
  1. package/.eslintrc +9 -0
  2. package/.storybook/main.js +20 -0
  3. package/.storybook/preview-head.html +31 -0
  4. package/.storybook/preview.js +21 -0
  5. package/package.json +40 -37
  6. package/src/components/Alert/Alert.module.scss +88 -0
  7. package/src/components/Alert/Alert.spec.tsx +78 -0
  8. package/src/components/Alert/Alert.stories.tsx +73 -0
  9. package/src/components/Alert/Alert.tsx +94 -0
  10. package/src/components/Alert/index.ts +1 -0
  11. package/src/components/Badge/Badge.module.scss +23 -0
  12. package/src/components/Badge/Badge.spec.tsx +12 -0
  13. package/src/components/Badge/Badge.stories.tsx +24 -0
  14. package/src/components/Badge/Badge.tsx +24 -0
  15. package/src/components/Badge/index.ts +1 -0
  16. package/src/components/Button/Button.module.scss +257 -0
  17. package/src/components/Button/Button.spec.tsx +110 -0
  18. package/src/components/Button/Button.stories.tsx +152 -0
  19. package/src/components/Button/Button.tsx +88 -0
  20. package/src/components/Button/index.ts +2 -0
  21. package/src/components/ButtonGroup/ButtonGroup.module.scss +31 -0
  22. package/src/components/ButtonGroup/ButtonGroup.spec.tsx +61 -0
  23. package/src/components/ButtonGroup/ButtonGroup.stories.tsx +53 -0
  24. package/src/components/ButtonGroup/ButtonGroup.tsx +75 -0
  25. package/src/components/ButtonGroup/index.ts +1 -0
  26. package/src/components/Card/Card.module.scss +31 -0
  27. package/src/components/Card/Card.spec.tsx +38 -0
  28. package/src/components/Card/Card.stories.tsx +30 -0
  29. package/src/components/Card/Card.tsx +34 -0
  30. package/src/components/Card/index.ts +2 -0
  31. package/src/components/Checkbox/Checkbox.module.scss +99 -0
  32. package/src/components/Checkbox/Checkbox.tsx +39 -0
  33. package/src/components/Checkbox/CheckboxField.spec.tsx +58 -0
  34. package/src/components/Checkbox/CheckboxField.stories.tsx +30 -0
  35. package/src/components/Checkbox/CheckboxField.tsx +46 -0
  36. package/src/components/Checkbox/index.ts +2 -0
  37. package/src/components/FieldDescription/FieldDescription.module.scss +5 -0
  38. package/src/components/FieldDescription/FieldDescription.tsx +23 -0
  39. package/src/components/FieldDescription/index.ts +1 -0
  40. package/src/components/FieldError/FieldError.module.scss +5 -0
  41. package/src/components/FieldError/FieldError.tsx +23 -0
  42. package/src/components/FieldError/index.ts +1 -0
  43. package/src/components/FieldGroup/FieldGroup.module.scss +25 -0
  44. package/src/components/FieldGroup/FieldGroup.spec.tsx +42 -0
  45. package/src/components/FieldGroup/FieldGroup.stories.tsx +34 -0
  46. package/src/components/FieldGroup/FieldGroup.tsx +38 -0
  47. package/src/components/FieldGroup/index.ts +1 -0
  48. package/src/components/Form/Form.module.scss +22 -0
  49. package/src/components/Form/Form.spec.tsx +38 -0
  50. package/src/components/Form/Form.stories.tsx +42 -0
  51. package/src/components/Form/Form.tsx +46 -0
  52. package/src/components/Form/index.ts +2 -0
  53. package/src/components/FormGroup/FormGroup.module.scss +20 -0
  54. package/src/components/FormGroup/FormGroup.spec.tsx +34 -0
  55. package/src/components/FormGroup/FormGroup.stories.tsx +30 -0
  56. package/src/components/FormGroup/FormGroup.tsx +44 -0
  57. package/src/components/FormGroup/index.ts +1 -0
  58. package/src/components/Icon/Icon.module.scss +70 -0
  59. package/src/components/Icon/Icon.stories.tsx +53 -0
  60. package/src/components/Icon/Icon.tsx +118 -0
  61. package/src/components/Icon/index.ts +1 -0
  62. package/src/components/InputField/InputField.module.scss +37 -0
  63. package/src/components/InputField/InputField.spec.tsx +53 -0
  64. package/src/components/InputField/InputField.stories.tsx +73 -0
  65. package/src/components/InputField/InputField.tsx +72 -0
  66. package/src/components/InputField/index.ts +1 -0
  67. package/src/components/Link/Link.module.scss +18 -0
  68. package/src/components/Link/Link.stories.tsx +17 -0
  69. package/src/components/Link/Link.tsx +37 -0
  70. package/src/components/Link/index.ts +1 -0
  71. package/src/components/Loader/Loader.module.scss +66 -0
  72. package/src/components/Loader/Loader.spec.tsx +64 -0
  73. package/src/components/Loader/Loader.stories.tsx +25 -0
  74. package/src/components/Loader/Loader.tsx +48 -0
  75. package/src/components/Loader/index.ts +1 -0
  76. package/src/components/Modal/ActionModal.tsx +37 -0
  77. package/src/components/Modal/Modal.module.scss +131 -0
  78. package/src/components/Modal/Modal.stories.tsx +85 -0
  79. package/src/components/Modal/Modal.tsx +41 -0
  80. package/src/components/Modal/ModalBase.spec.tsx +58 -0
  81. package/src/components/Modal/ModalBase.tsx +66 -0
  82. package/src/components/Modal/ModalCloseButton.tsx +20 -0
  83. package/src/components/Modal/ModalPortal.tsx +35 -0
  84. package/src/components/Modal/index.ts +11 -0
  85. package/src/components/NumericInput/NumericInput.module.scss +110 -0
  86. package/src/components/NumericInput/NumericInput.spec.tsx +129 -0
  87. package/src/components/NumericInput/NumericInput.stories.tsx +56 -0
  88. package/src/components/NumericInput/NumericInput.tsx +161 -0
  89. package/src/components/NumericInput/NumericInputField.stories.tsx +58 -0
  90. package/src/components/NumericInput/NumericInputField.tsx +57 -0
  91. package/src/components/NumericInput/index.ts +2 -0
  92. package/src/components/Popover/Popover.module.scss +16 -0
  93. package/src/components/Popover/Popover.spec.tsx +39 -0
  94. package/src/components/Popover/Popover.stories.css +23 -0
  95. package/src/components/Popover/Popover.stories.tsx +164 -0
  96. package/src/components/Popover/Popover.tsx +124 -0
  97. package/src/components/Popover/index.ts +1 -0
  98. package/src/components/Progress/ProgressBar.module.scss +48 -0
  99. package/src/components/Progress/ProgressBar.spec.tsx +33 -0
  100. package/src/components/Progress/ProgressBar.stories.tsx +23 -0
  101. package/src/components/Progress/ProgressBar.tsx +62 -0
  102. package/src/components/Progress/ProgressCircle.module.scss +64 -0
  103. package/src/components/Progress/ProgressCircle.spec.tsx +34 -0
  104. package/src/components/Progress/ProgressCircle.stories.tsx +28 -0
  105. package/src/components/Progress/ProgressCircle.tsx +104 -0
  106. package/src/components/Progress/constants.ts +23 -0
  107. package/src/components/Progress/helpers.ts +23 -0
  108. package/src/components/Progress/index.ts +1 -0
  109. package/src/components/PromoBanner/PromoBanner.module.scss +121 -0
  110. package/src/components/PromoBanner/PromoBanner.spec.tsx +119 -0
  111. package/src/components/PromoBanner/PromoBanner.stories.tsx +125 -0
  112. package/src/components/PromoBanner/PromoBanner.tsx +99 -0
  113. package/src/components/PromoBanner/index.ts +1 -0
  114. package/src/components/RadioButton/RadioButton.module.scss +100 -0
  115. package/src/components/RadioButton/RadioButton.spec.tsx +70 -0
  116. package/src/components/RadioButton/RadioButton.stories.tsx +34 -0
  117. package/src/components/RadioButton/RadioButton.tsx +54 -0
  118. package/src/components/RadioButton/index.ts +1 -0
  119. package/src/components/Switch/Switch.module.scss +126 -0
  120. package/src/components/Switch/Switch.spec.tsx +40 -0
  121. package/src/components/Switch/Switch.stories.tsx +23 -0
  122. package/src/components/Switch/Switch.tsx +100 -0
  123. package/src/components/Switch/index.ts +1 -0
  124. package/src/components/Tab/Tab.module.scss +56 -0
  125. package/src/components/Tab/Tab.spec.tsx +34 -0
  126. package/src/components/Tab/Tab.stories.tsx +20 -0
  127. package/src/components/Tab/Tab.tsx +49 -0
  128. package/src/components/Tab/TabsWrapper.module.scss +11 -0
  129. package/src/components/Tab/TabsWrapper.spec.tsx +19 -0
  130. package/src/components/Tab/TabsWrapper.stories.tsx +46 -0
  131. package/src/components/Tab/TabsWrapper.tsx +24 -0
  132. package/src/components/Tab/index.ts +2 -0
  133. package/src/components/Tag/Tag.module.scss +131 -0
  134. package/src/components/Tag/Tag.spec.tsx +93 -0
  135. package/src/components/Tag/Tag.stories.tsx +151 -0
  136. package/src/components/Tag/Tag.tsx +129 -0
  137. package/src/components/Tag/index.ts +1 -0
  138. package/src/components/TagInput/EditableTag.tsx +54 -0
  139. package/src/components/TagInput/EditableTagContent.tsx +81 -0
  140. package/src/components/TagInput/EmailTagInput.tsx +31 -0
  141. package/src/components/TagInput/TagInput.module.scss +81 -0
  142. package/src/components/TagInput/TagInput.spec.tsx +58 -0
  143. package/src/components/TagInput/TagInput.stories.tsx +49 -0
  144. package/src/components/TagInput/TagInput.tsx +133 -0
  145. package/src/components/TagInput/index.ts +4 -0
  146. package/src/components/TextField/TextField.module.scss +90 -0
  147. package/src/components/TextField/TextField.spec.tsx +40 -0
  148. package/src/components/TextField/TextField.stories.tsx +60 -0
  149. package/src/components/TextField/TextField.tsx +100 -0
  150. package/src/components/TextField/index.ts +1 -0
  151. package/src/components/Toast/Toast.module.scss +218 -0
  152. package/src/components/Toast/Toast.spec.tsx +96 -0
  153. package/src/components/Toast/Toast.stories.tsx +64 -0
  154. package/src/components/Toast/Toast.tsx +121 -0
  155. package/src/components/Toast/ToastWrapper.spec.tsx +13 -0
  156. package/src/components/Toast/ToastWrapper.stories.tsx +55 -0
  157. package/src/components/Toast/ToastWrapper.tsx +87 -0
  158. package/src/components/Toast/index.ts +2 -0
  159. package/src/components/Typography/Heading.tsx +36 -0
  160. package/src/components/Typography/Text.tsx +49 -0
  161. package/src/components/Typography/Typography.module.scss +132 -0
  162. package/src/components/Typography/index.ts +2 -0
  163. package/src/index.scss +9 -0
  164. package/src/index.ts +32 -0
  165. package/src/stories/ColorTokens.stories.mdx +10 -0
  166. package/src/stories/Icons.stories.mdx +12 -0
  167. package/src/stories/Typography.stories.mdx +65 -0
  168. package/src/stories/components/ColorTokens.tsx +99 -0
  169. package/src/stories/components/IconsShowcase.tsx +25 -0
  170. package/src/stories/components/iconsShowcase.css +13 -0
  171. package/src/test/setup.ts +1 -0
  172. package/{dist/test-utils.d.ts → src/test/utils.ts} +3 -2
  173. package/src/themes/dark.scss +127 -0
  174. package/src/themes/designTokens.ts +126 -0
  175. package/src/themes/legacy.scss +128 -0
  176. package/src/themes/light.scss +127 -0
  177. package/src/utils/keyCodes.ts +12 -0
  178. package/src/utils/noop.ts +2 -0
  179. package/src/vite-env.d.ts +1 -0
  180. package/tsconfig.json +25 -0
  181. package/tsconfig.node.json +8 -0
  182. package/vite.config.ts +44 -0
  183. package/dist/components/Alert.d.ts +0 -19
  184. package/dist/components/Alert.spec.d.ts +0 -1
  185. package/dist/components/Badge.d.ts +0 -6
  186. package/dist/components/Badge.spec.d.ts +0 -1
  187. package/dist/components/Button.d.ts +0 -13
  188. package/dist/components/Button.spec.d.ts +0 -1
  189. package/dist/components/ButtonGroup.d.ts +0 -11
  190. package/dist/components/ButtonGroup.spec.d.ts +0 -1
  191. package/dist/components/Card.d.ts +0 -6
  192. package/dist/components/Card.spec.d.ts +0 -1
  193. package/dist/components/Checkbox.d.ts +0 -7
  194. package/dist/components/CheckboxField.d.ts +0 -7
  195. package/dist/components/CheckboxField.spec.d.ts +0 -1
  196. package/dist/components/FieldDescription.d.ts +0 -3
  197. package/dist/components/FieldError.d.ts +0 -3
  198. package/dist/components/FieldGroup.d.ts +0 -8
  199. package/dist/components/FieldGroup.spec.d.ts +0 -1
  200. package/dist/components/FieldLabel.d.ts +0 -3
  201. package/dist/components/Form.d.ts +0 -7
  202. package/dist/components/Form.spec.d.ts +0 -1
  203. package/dist/components/FormGroup.d.ts +0 -7
  204. package/dist/components/FormGroup.spec.d.ts +0 -1
  205. package/dist/components/Heading.d.ts +0 -11
  206. package/dist/components/Icon.d.ts +0 -69
  207. package/dist/components/Loader.d.ts +0 -10
  208. package/dist/components/Loader.spec.d.ts +0 -1
  209. package/dist/components/Modal/ActionModal.d.ts +0 -8
  210. package/dist/components/Modal/Modal.d.ts +0 -7
  211. package/dist/components/Modal/ModalBase.d.ts +0 -6
  212. package/dist/components/Modal/ModalBase.spec.d.ts +0 -1
  213. package/dist/components/Modal/ModalCloseButton.d.ts +0 -3
  214. package/dist/components/Modal/ModalPortal.d.ts +0 -7
  215. package/dist/components/ProgressBar.d.ts +0 -9
  216. package/dist/components/ProgressBar.spec.d.ts +0 -1
  217. package/dist/components/ProgressCircle.d.ts +0 -9
  218. package/dist/components/ProgressCircle.spec.d.ts +0 -1
  219. package/dist/components/PromoBanner.d.ts +0 -19
  220. package/dist/components/PromoBanner.spec.d.ts +0 -1
  221. package/dist/components/RadioButton.d.ts +0 -7
  222. package/dist/components/RadioButton.spec.d.ts +0 -1
  223. package/dist/components/Switch.d.ts +0 -17
  224. package/dist/components/Switch.spec.d.ts +0 -1
  225. package/dist/components/Tab.d.ts +0 -12
  226. package/dist/components/Tab.spec.d.ts +0 -1
  227. package/dist/components/TabsWrapper.d.ts +0 -3
  228. package/dist/components/TabsWrapper.spec.d.ts +0 -1
  229. package/dist/components/Text.d.ts +0 -15
  230. package/dist/components/Toast.d.ts +0 -22
  231. package/dist/components/Toast.spec.d.ts +0 -1
  232. package/dist/components/ToastWrapper.d.ts +0 -26
  233. package/dist/components/ToastWrapper.spec.d.ts +0 -1
  234. package/dist/components/constants.d.ts +0 -12
  235. package/dist/components/helpers.d.ts +0 -3
  236. package/dist/constants/keyCodes.d.ts +0 -3
  237. package/dist/index.css +0 -2
  238. package/dist/index.css.map +0 -1
  239. package/dist/index.d.ts +0 -8
  240. package/dist/index.js +0 -2
  241. package/dist/index.js.map +0 -1
  242. package/dist/index.modern.js +0 -2
  243. package/dist/index.modern.js.map +0 -1
  244. package/dist/index.module.js +0 -2
  245. package/dist/index.module.js.map +0 -1
  246. package/dist/index.umd.js +0 -2
  247. package/dist/index.umd.js.map +0 -1
  248. package/dist/stories/Alert.stories.d.ts +0 -14
  249. package/dist/stories/Badge.stories.d.ts +0 -16
  250. package/dist/stories/Button.stories.d.ts +0 -20
  251. package/dist/stories/ButtonGroup.stories.d.ts +0 -18
  252. package/dist/stories/Card.stories.d.ts +0 -10
  253. package/dist/stories/CheckboxField.stories.d.ts +0 -9
  254. package/dist/stories/FieldGroup.stories.d.ts +0 -9
  255. package/dist/stories/Form.stories.d.ts +0 -6
  256. package/dist/stories/FormGroup.stories.d.ts +0 -9
  257. package/dist/stories/Icon.stories.d.ts +0 -14
  258. package/dist/stories/Loader.stories.d.ts +0 -8
  259. package/dist/stories/Modal.stories.d.ts +0 -19
  260. package/dist/stories/ProgressCircle.stories.d.ts +0 -13
  261. package/dist/stories/Progressbar.stories.d.ts +0 -13
  262. package/dist/stories/PromoBanner.stories.d.ts +0 -13
  263. package/dist/stories/RadioButton.stories.d.ts +0 -9
  264. package/dist/stories/Switch.stories.d.ts +0 -12
  265. package/dist/stories/Tab.stories.d.ts +0 -10
  266. package/dist/stories/TabsWrapper.stories.d.ts +0 -15
  267. package/dist/stories/Toast.stories.d.ts +0 -15
  268. package/dist/stories/ToastWrapper.stories.d.ts +0 -40
  269. package/dist/stories/foundations/components/ColorTokens.d.ts +0 -2
  270. package/dist/stories/foundations/components/IconsShowcase.d.ts +0 -6
  271. package/dist/utils/noop.d.ts +0 -1
@@ -0,0 +1,125 @@
1
+ import * as React from 'react';
2
+ import { ComponentMeta, Story } from '@storybook/react';
3
+
4
+ import {
5
+ PromoBannerProps,
6
+ PromoBanner as PromoBannerComponent,
7
+ PromoBannerSize,
8
+ } from './PromoBanner';
9
+
10
+ export default {
11
+ title: 'Components/PromoBanner',
12
+ component: PromoBannerComponent,
13
+ parameters: {
14
+ componentSubtitle: `
15
+ Promo banner component is used to let users know about upgrade options, new features,
16
+ or any other information that doesn't require immediate action. This component should be
17
+ used wisely so don't overuse it too often. Remember to always specify the action that you
18
+ want to promote.
19
+ `,
20
+ },
21
+ argTypes: { onClose: { defaultValue: null } },
22
+ } as ComponentMeta<typeof PromoBannerComponent>;
23
+
24
+ const StoryTemplate: Story<PromoBannerProps> = (args: PromoBannerProps) => (
25
+ <div>
26
+ <PromoBannerComponent {...args}>
27
+ <>
28
+ A description with a <b>maximum of 100 characters</b>. That usually
29
+ means only one or two sentences.
30
+ </>
31
+ </PromoBannerComponent>
32
+ </div>
33
+ );
34
+
35
+ export const PromoBanner = StoryTemplate.bind({});
36
+ PromoBanner.args = {
37
+ header: 'This example headline has 40 characters',
38
+ };
39
+
40
+ export const PromoBannerWithButtons = StoryTemplate.bind({});
41
+ PromoBannerWithButtons.args = {
42
+ header: 'This example headline has 40 characters',
43
+ buttonText: 'Example',
44
+ linkText: 'Example',
45
+ onButtonClick: () => {
46
+ alert('onButtonClick click');
47
+ },
48
+ onLinkClick: () => {
49
+ alert('onLinkClick click');
50
+ },
51
+ };
52
+
53
+ export const PromoBannerWithImage = StoryTemplate.bind({});
54
+ PromoBannerWithImage.args = {
55
+ header: 'This example headline has 40 characters',
56
+ img: 'https://via.placeholder.com/100',
57
+ };
58
+
59
+ export const PromoBannerLight = StoryTemplate.bind({});
60
+ PromoBannerLight.args = {
61
+ header: 'This example headline has 40 characters',
62
+ light: true,
63
+ };
64
+
65
+ export const PromoBannerWithClose = StoryTemplate.bind({});
66
+ PromoBannerWithClose.args = {
67
+ header: 'This example headline has 40 characters',
68
+ onClose: () => {
69
+ alert('onClose click');
70
+ },
71
+ };
72
+
73
+ export const PromoBannerSmall = StoryTemplate.bind({});
74
+ PromoBannerSmall.args = {
75
+ header: 'This example headline has 40 characters',
76
+ img: 'https://via.placeholder.com/100',
77
+ size: PromoBannerSize.Small,
78
+ buttonText: 'Example button',
79
+ linkText: 'Example link',
80
+ onClose: () => {
81
+ alert('onClose click');
82
+ },
83
+ onButtonClick: () => {
84
+ alert('onButtonClick click');
85
+ },
86
+ onLinkClick: () => {
87
+ alert('onLinkClick click');
88
+ },
89
+ };
90
+
91
+ export const PromoBannerMedium = StoryTemplate.bind({});
92
+ PromoBannerMedium.args = {
93
+ header: 'This example headline has 40 characters',
94
+ img: 'https://via.placeholder.com/100',
95
+ size: PromoBannerSize.Medium,
96
+ buttonText: 'Example button',
97
+ linkText: 'Example link',
98
+ onClose: () => {
99
+ alert('onClose click');
100
+ },
101
+ onButtonClick: () => {
102
+ alert('onButtonClick click');
103
+ },
104
+ onLinkClick: () => {
105
+ alert('onLinkClick click');
106
+ },
107
+ };
108
+
109
+ export const PromoBannerLarge = StoryTemplate.bind({});
110
+ PromoBannerLarge.args = {
111
+ header: 'This example headline has 40 characters',
112
+ img: 'https://via.placeholder.com/100',
113
+ size: PromoBannerSize.Large,
114
+ buttonText: 'Example button',
115
+ linkText: 'Example link',
116
+ onClose: () => {
117
+ alert('onClose click');
118
+ },
119
+ onButtonClick: () => {
120
+ alert('onButtonClick click');
121
+ },
122
+ onLinkClick: () => {
123
+ alert('onLinkClick click');
124
+ },
125
+ };
@@ -0,0 +1,99 @@
1
+ import * as React from 'react';
2
+ import cx from 'clsx';
3
+ import { Close as CloseIcon } from '@livechat/design-system-icons/react/material';
4
+
5
+ import { Button } from '../Button';
6
+ import { Icon, IconSizeName } from '../Icon';
7
+
8
+ import styles from './PromoBanner.module.scss';
9
+
10
+ const baseClass = 'promo-banner';
11
+
12
+ export const enum PromoBannerSize {
13
+ Small = 'small',
14
+ Medium = 'medium',
15
+ Large = 'large',
16
+ }
17
+
18
+ export interface PromoBannerProps {
19
+ className?: string;
20
+ buttonText?: string;
21
+ header: string;
22
+ img?: string;
23
+ light?: boolean;
24
+ linkText?: string;
25
+ size?: PromoBannerSize;
26
+ onButtonClick?: () => void;
27
+ onClose?: () => void;
28
+ onLinkClick?: () => void;
29
+ }
30
+
31
+ export const PromoBanner: React.FC<PromoBannerProps> = ({
32
+ className,
33
+ buttonText,
34
+ children,
35
+ header,
36
+ img,
37
+ light = false,
38
+ linkText,
39
+ size = PromoBannerSize.Small,
40
+ onButtonClick,
41
+ onClose,
42
+ onLinkClick,
43
+ }) => {
44
+ const mergedClassNames = cx(
45
+ styles[baseClass],
46
+ {
47
+ [styles[`${baseClass}--light`]]: light,
48
+ [styles[`${baseClass}--${size}`]]: size,
49
+ },
50
+ className
51
+ );
52
+
53
+ const shouldRenderLargeFooter = (buttonText || linkText) && size === 'large';
54
+ const shouldRenderSmallOrMediumFooter =
55
+ (buttonText || linkText) && size !== 'large';
56
+
57
+ const footer = (
58
+ <div className={styles[`${baseClass}__footer`]}>
59
+ {buttonText && (
60
+ <Button
61
+ kind="primary"
62
+ size="compact"
63
+ onClick={onButtonClick}
64
+ className={styles[`${baseClass}__button-text`]}
65
+ >
66
+ {buttonText}
67
+ </Button>
68
+ )}
69
+ {linkText && (
70
+ <Button size="compact" kind="text" onClick={onLinkClick}>
71
+ {linkText}
72
+ </Button>
73
+ )}
74
+ </div>
75
+ );
76
+
77
+ return (
78
+ <div className={mergedClassNames}>
79
+ <div className={styles[`${baseClass}__content`]}>
80
+ {img && <img src={img} className={styles[`${baseClass}__img`]} />}
81
+ <div className={styles[`${baseClass}__wrapper`]}>
82
+ <div className={styles[`${baseClass}__header`]}>{header}</div>
83
+ <div>{children}</div>
84
+ {shouldRenderSmallOrMediumFooter && footer}
85
+ </div>
86
+ {shouldRenderLargeFooter && footer}
87
+ </div>
88
+ {onClose && (
89
+ <button
90
+ type="button"
91
+ className={styles[`${baseClass}__close-icon`]}
92
+ onClick={onClose}
93
+ >
94
+ <Icon source={CloseIcon} size={IconSizeName.Large} />
95
+ </button>
96
+ )}
97
+ </div>
98
+ );
99
+ };
@@ -0,0 +1 @@
1
+ export { PromoBanner } from './PromoBanner';
@@ -0,0 +1,100 @@
1
+ .radio-button {
2
+ display: inline-block;
3
+
4
+ &__label {
5
+ align-items: center;
6
+ cursor: pointer;
7
+ display: inline-flex;
8
+ flex: 0 1 auto;
9
+ width: 100%;
10
+ }
11
+
12
+ &__circle {
13
+ background-color: var(--surface-basic-default);
14
+ border: 1px solid var(--border-default);
15
+ border-radius: 16px;
16
+ box-sizing: border-box;
17
+ height: 16px;
18
+ margin-right: 8px;
19
+ position: relative;
20
+ transition: all 0.2s ease;
21
+ width: 16px;
22
+ }
23
+
24
+ &__inner-circle {
25
+ background-color: var(--color-action-default);
26
+ border-radius: 8px;
27
+ height: 8px;
28
+ left: 3px;
29
+ opacity: 0;
30
+ position: absolute;
31
+ top: 3px;
32
+ transition: all 0.2s ease;
33
+ width: 8px;
34
+ }
35
+
36
+ &__text {
37
+ color: var(--content-default);
38
+ flex: 1 0 auto;
39
+ }
40
+
41
+ &__input {
42
+ cursor: pointer;
43
+ height: 100%;
44
+ margin: 0;
45
+ opacity: 0;
46
+ width: 100%;
47
+ }
48
+
49
+ &__helper {
50
+ cursor: default;
51
+ margin-left: 24px;
52
+ }
53
+
54
+ &:focus {
55
+ .radio-button__circle {
56
+ border-radius: 20px;
57
+ box-shadow: 0 0 1px 1.6px rgba(67, 132, 245, 0.7); // TODO use color token
58
+ }
59
+ }
60
+
61
+ &--selected {
62
+ .radio-button__circle {
63
+ border-color: var(--color-action-default);
64
+ }
65
+
66
+ .radio-button__inner-circle {
67
+ opacity: 1;
68
+ }
69
+ }
70
+
71
+ &--selected:hover {
72
+ .radio-button__circle {
73
+ border-color: var(--color-action-hover);
74
+ }
75
+
76
+ .radio-button__inner-circle {
77
+ background-color: var(--color-action-hover);
78
+ }
79
+ }
80
+
81
+ &--disabled {
82
+ .radio-button__circle {
83
+ opacity: 0.4;
84
+ }
85
+
86
+ &:not(.radio-button--selected) {
87
+ .radio-button__circle {
88
+ background-color: var(--surface-basic-disabled);
89
+ }
90
+ }
91
+
92
+ .radio-button__label,
93
+ .radio-button__input,
94
+ .radio-button__text,
95
+ .radio-button__helper {
96
+ color: var(--content-disabled);
97
+ cursor: not-allowed;
98
+ }
99
+ }
100
+ }
@@ -0,0 +1,70 @@
1
+ import * as React from 'react';
2
+ import { render, userEvent, vi } from 'test-utils';
3
+ import { RadioButton } from './RadioButton';
4
+
5
+ import styles from './RadioButton.module.scss';
6
+
7
+ const baseClass = 'radio-button';
8
+
9
+ describe('<RadioButton> component', () => {
10
+ it('should allow for custom class', () => {
11
+ const { container } = render(
12
+ <RadioButton className="my-css-class">test</RadioButton>
13
+ );
14
+ expect(container.firstChild).toHaveClass('my-css-class');
15
+ });
16
+
17
+ it('should not have been checked or disabled by default', () => {
18
+ const { container } = render(<RadioButton>test</RadioButton>);
19
+ expect(container.firstChild).not.toHaveClass(
20
+ styles[`${baseClass}--selected`]
21
+ );
22
+ expect(container.firstChild).not.toHaveClass(
23
+ styles[`${baseClass}--disabled`]
24
+ );
25
+ });
26
+
27
+ it('should have secondary class when used with secondary prop', () => {
28
+ const { container } = render(
29
+ <RadioButton checked disabled>
30
+ test
31
+ </RadioButton>
32
+ );
33
+ expect(container.firstChild).toHaveClass(styles[`${baseClass}--selected`]);
34
+ expect(container.firstChild).toHaveClass(styles[`${baseClass}--disabled`]);
35
+ });
36
+
37
+ it('should call onChange method', () => {
38
+ const onChange = vi.fn();
39
+
40
+ const { getByRole } = render(
41
+ <RadioButton onChange={onChange}>test</RadioButton>
42
+ );
43
+ userEvent.click(getByRole('radio'));
44
+ expect(onChange).toBeCalledTimes(1);
45
+ });
46
+
47
+ it('should not call onChange method when checked', () => {
48
+ const onChange = vi.fn();
49
+
50
+ const { getByRole } = render(
51
+ <RadioButton checked onChange={onChange}>
52
+ test
53
+ </RadioButton>
54
+ );
55
+ userEvent.click(getByRole('radio'));
56
+ expect(onChange).not.toBeCalled();
57
+ });
58
+
59
+ it('should not call onChange method when disabled', () => {
60
+ const onChange = vi.fn();
61
+
62
+ const { getByRole } = render(
63
+ <RadioButton disabled onChange={onChange}>
64
+ test
65
+ </RadioButton>
66
+ );
67
+ userEvent.click(getByRole('radio'));
68
+ expect(onChange).not.toBeCalled();
69
+ });
70
+ });
@@ -0,0 +1,34 @@
1
+ import * as React from 'react';
2
+ import { ComponentMeta } from '@storybook/react';
3
+
4
+ import {
5
+ RadioButton as RadioButtonComponent,
6
+ RadioButtonProps,
7
+ } from './RadioButton';
8
+
9
+ export default {
10
+ title: 'Forms (WIP)/Radio Button',
11
+ component: RadioButtonComponent,
12
+ argTypes: { onChange: { action: 'changed' } },
13
+ parameters: {
14
+ componentSubtitle: `
15
+ Use radio buttons where there’s a list of two or more mutually exclusive items, and the users must select exactly one option.
16
+ `,
17
+ },
18
+ } as ComponentMeta<typeof RadioButtonComponent>;
19
+
20
+ export const RadioButton = ({
21
+ children,
22
+ ...args
23
+ }: RadioButtonProps): React.ReactElement => (
24
+ <div>
25
+ <RadioButtonComponent {...args}>{children}</RadioButtonComponent>
26
+ </div>
27
+ );
28
+
29
+ RadioButton.args = {
30
+ checked: false,
31
+ disabled: false,
32
+ description: 'Help text',
33
+ children: 'Radio label',
34
+ } as RadioButtonProps;
@@ -0,0 +1,54 @@
1
+ import * as React from 'react';
2
+ import cx from 'clsx';
3
+ import { FieldDescription } from '../FieldDescription';
4
+ import { Text } from '../Typography';
5
+
6
+ import styles from './RadioButton.module.scss';
7
+
8
+ export interface RadioButtonProps
9
+ extends React.HTMLAttributes<HTMLInputElement> {
10
+ description?: React.ReactNode;
11
+ checked?: boolean;
12
+ disabled?: boolean;
13
+ }
14
+
15
+ const baseClass = 'radio-button';
16
+
17
+ export const RadioButton: React.FC<RadioButtonProps> = ({
18
+ children,
19
+ className = '',
20
+ description,
21
+ checked,
22
+ disabled,
23
+ ...props
24
+ }) => {
25
+ const mergedClassNames = cx(styles[baseClass], className, {
26
+ [styles[`${baseClass}--selected`]]: checked,
27
+ [styles[`${baseClass}--disabled`]]: disabled,
28
+ });
29
+
30
+ return (
31
+ <div className={mergedClassNames}>
32
+ <label className={styles[`${baseClass}__label`]}>
33
+ <div className={styles[`${baseClass}__circle`]}>
34
+ <span className={styles[`${baseClass}__inner-circle`]} />
35
+ <input
36
+ className={styles[`${baseClass}__input`]}
37
+ {...props}
38
+ type="radio"
39
+ checked={checked}
40
+ disabled={disabled}
41
+ />
42
+ </div>
43
+ <Text as="div" size="md" className={styles[`${baseClass}__text`]}>
44
+ {children}
45
+ </Text>
46
+ </label>
47
+ {description && (
48
+ <FieldDescription className={styles[`${baseClass}__helper`]}>
49
+ {description}
50
+ </FieldDescription>
51
+ )}
52
+ </div>
53
+ );
54
+ };
@@ -0,0 +1 @@
1
+ export { RadioButton } from './RadioButton';
@@ -0,0 +1,126 @@
1
+ $switch-transition-duration: 300ms;
2
+ $switch-transition-timing-function: cubic-bezier(0.33, 0, 0.67, 1);
3
+
4
+ $base-class: 'switch';
5
+ $basic-height: 28px;
6
+ $basic-width: 44px;
7
+ $basic-size: $basic-height - 4px;
8
+ $basic-move: $basic-width - $basic-size;
9
+
10
+ $compact-height: 16px;
11
+ $compact-width: 25px;
12
+ $compact-size: $compact-height - 2px;
13
+ $compact-move: $compact-width - $compact-size;
14
+
15
+ /* stylelint-disable max-nesting-depth */
16
+ .#{$base-class} {
17
+ display: inline-block;
18
+ outline: none;
19
+ position: relative;
20
+
21
+ &:hover {
22
+ .#{$base-class}__track {
23
+ &--enabled {
24
+ background-color: var(--color-positive-default);
25
+ }
26
+
27
+ &--disabled {
28
+ background-color: var(--surface-secondary-default);
29
+ }
30
+ }
31
+ }
32
+
33
+ &--basic {
34
+ height: $basic-height;
35
+ width: $basic-width;
36
+ }
37
+
38
+ &--compact {
39
+ height: $compact-height;
40
+ width: $compact-width;
41
+ }
42
+
43
+ &__input {
44
+ cursor: pointer;
45
+ height: 100%;
46
+ margin: 0;
47
+ opacity: 0;
48
+ width: 100%;
49
+ }
50
+
51
+ &__input:focus + .#{$base-class}__container {
52
+ .#{$base-class}__track {
53
+ //TODO use shadow token instead of hex color
54
+ box-shadow: 0 0 1px 2px rgba(#4379d6, 0.7);
55
+ }
56
+ }
57
+
58
+ &__container {
59
+ align-items: center;
60
+ display: flex;
61
+ height: 100%;
62
+ left: 0;
63
+ pointer-events: none;
64
+ position: absolute;
65
+ top: 0;
66
+ width: 100%;
67
+ }
68
+
69
+ &__track {
70
+ border-radius: 24px;
71
+ flex: 1;
72
+ height: 100%;
73
+ transition-duration: $switch-transition-duration;
74
+ transition-property: background-color;
75
+ transition-timing-function: $switch-transition-timing-function;
76
+
77
+ &--enabled {
78
+ background-color: var(--color-positive-default);
79
+ }
80
+
81
+ &--disabled {
82
+ background-color: var(--surface-secondary-default);
83
+ }
84
+ }
85
+
86
+ &__slider {
87
+ background: var(--content-white-locked);
88
+ border-radius: 50%;
89
+ position: absolute;
90
+ top: 50%;
91
+ transform: translateY(-50%);
92
+ transition-duration: $switch-transition-duration;
93
+ transition-property: left, right;
94
+ transition-timing-function: $switch-transition-timing-function;
95
+
96
+ &--basic {
97
+ height: calc(#{$basic-size});
98
+ width: calc(#{$basic-size});
99
+
100
+ &--enabled {
101
+ left: calc(#{$basic-move} - 2px);
102
+ right: 2px;
103
+ }
104
+
105
+ &--disabled {
106
+ left: 2px;
107
+ right: calc(#{$basic-move} - 2px);
108
+ }
109
+ }
110
+
111
+ &--compact {
112
+ height: calc(#{$compact-size});
113
+ width: calc(#{$compact-size});
114
+
115
+ &--enabled {
116
+ left: calc(#{$compact-move} - 2px);
117
+ right: 2px;
118
+ }
119
+
120
+ &--disabled {
121
+ left: 2px;
122
+ right: calc(#{$compact-move} - 2px);
123
+ }
124
+ }
125
+ }
126
+ }
@@ -0,0 +1,40 @@
1
+ import * as React from 'react';
2
+ import { render, fireEvent, vi } from 'test-utils';
3
+ import { Switch } from './Switch';
4
+
5
+ describe('Switch', () => {
6
+ it('should call onChange without changing state when custom handler is passed', () => {
7
+ const toggleFn = vi.fn();
8
+ const { getByRole } = render(<Switch onChange={toggleFn} />);
9
+ const checkbox = getByRole('checkbox') as HTMLInputElement;
10
+ expect(checkbox.checked).toEqual(false);
11
+ fireEvent.click(checkbox);
12
+ expect(toggleFn).toHaveBeenCalled();
13
+ expect(checkbox.checked).toEqual(false);
14
+ });
15
+
16
+ it('should change state to enabled when user clicks on disabled switch', () => {
17
+ const { getByRole } = render(<Switch />);
18
+ const checkbox = getByRole('checkbox') as HTMLInputElement;
19
+ expect(checkbox.checked).toEqual(false);
20
+ fireEvent.click(checkbox);
21
+ expect(checkbox.checked).toEqual(true);
22
+ });
23
+
24
+ it('should change state to disabled when user clicks on enabled switch', () => {
25
+ const { getByRole } = render(<Switch on={true} />);
26
+ const checkbox = getByRole('checkbox') as HTMLInputElement;
27
+ expect(checkbox.checked).toEqual(true);
28
+ fireEvent.click(checkbox);
29
+ expect(checkbox.checked).toEqual(false);
30
+ });
31
+
32
+ it('should be enabled if defaultOn is set to true', () => {
33
+ const { getByRole } = render(<Switch defaultOn={true} />);
34
+ const checkbox = getByRole('checkbox') as HTMLInputElement;
35
+ expect(checkbox.checked).toEqual(true);
36
+ fireEvent.click(checkbox);
37
+ expect(checkbox.checked).toEqual(false);
38
+ });
39
+ });
40
+ 1;
@@ -0,0 +1,23 @@
1
+ import * as React from 'react';
2
+ import { ComponentMeta } from '@storybook/react';
3
+
4
+ import { Switch as SwitchComponent, SwitchProps, SwitchSize } from './Switch';
5
+ import noop from '../../utils/noop';
6
+
7
+ export default {
8
+ title: 'Components/Switch',
9
+ component: SwitchComponent,
10
+ } as ComponentMeta<typeof SwitchComponent>;
11
+
12
+ export const Switch = (args: SwitchProps): React.ReactElement => {
13
+ return (
14
+ <div>
15
+ <SwitchComponent {...args} />
16
+ </div>
17
+ );
18
+ };
19
+
20
+ Switch.args = {
21
+ size: SwitchSize.Basic,
22
+ onChange: noop,
23
+ };