@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,257 @@
1
+ $base-class: 'btn';
2
+
3
+ .#{$base-class} {
4
+ align-items: center;
5
+ border-radius: 4px;
6
+ border-style: solid;
7
+ border-width: 1px;
8
+ cursor: pointer;
9
+ display: inline-flex;
10
+ font-size: 15px;
11
+ font-weight: 600;
12
+ height: 36px;
13
+ justify-content: center;
14
+ min-width: 36px;
15
+ padding: 8px 16px;
16
+ position: relative;
17
+ text-align: center;
18
+ text-decoration: none;
19
+ transition-duration: 200ms;
20
+ transition-property: opacity, border, color, background-color, box-shadow;
21
+ transition-timing-function: cubic-bezier(0.64, 0, 0.35, 1);
22
+ user-select: none;
23
+
24
+ &:focus,
25
+ &:hover,
26
+ &:active {
27
+ outline: 0;
28
+ }
29
+
30
+ &:focus {
31
+ box-shadow: 0 0 1px 2px rgba(var(--color-action-default-rgb), 0.7);
32
+ }
33
+
34
+ &--disabled {
35
+ cursor: not-allowed;
36
+ }
37
+
38
+ &--icon-only {
39
+ padding: 6px;
40
+
41
+ .#{$base-class}__icon--left,
42
+ .#{$base-class}__icon--right {
43
+ margin: 0;
44
+ }
45
+ }
46
+
47
+ &--full-width {
48
+ width: 100%;
49
+ }
50
+
51
+ &--basic {
52
+ background-color: var(--surface-basic-default);
53
+ border-color: var(--color-action-default);
54
+ color: var(--color-action-default);
55
+
56
+ &:hover {
57
+ background-color: var(--surface-basic-subtle);
58
+ color: var(--color-action-hover);
59
+ }
60
+
61
+ &:active {
62
+ background-color: var(--surface-feedback-info);
63
+ color: var(--color-action-hover);
64
+ }
65
+
66
+ &[disabled] {
67
+ background-color: inherit;
68
+ border-color: var(--color-action-disabled);
69
+ color: var(--color-action-disabled);
70
+ }
71
+ }
72
+
73
+ &--primary {
74
+ background-color: var(--color-action-default);
75
+ border-color: var(--color-action-default);
76
+ color: var(--content-invert-default);
77
+
78
+ &:hover {
79
+ background-color: var(--color-action-hover);
80
+ border-color: var(--color-action-hover);
81
+ }
82
+
83
+ &:active {
84
+ background-color: var(--color-action-active);
85
+ border-color: var(--color-action-active);
86
+ }
87
+
88
+ &[disabled] {
89
+ background-color: var(--color-action-disabled);
90
+ border-color: var(--color-action-disabled);
91
+ }
92
+ }
93
+
94
+ &--secondary {
95
+ background-color: var(--surface-basic-default);
96
+ border-color: var(--surface-secondary-default);
97
+ color: var(--content-default);
98
+
99
+ &:hover {
100
+ background-color: var(--surface-basic-hover);
101
+ border-color: var(--surface-secondary-hover);
102
+ }
103
+
104
+ &:active {
105
+ background-color: var(--surface-feedback-info);
106
+ border-color: var(--color-action-default);
107
+ color: var(--color-action-default);
108
+ }
109
+
110
+ &[disabled] {
111
+ background-color: inherit;
112
+ border-color: var(--border-disabled);
113
+ color: var(--content-disabled);
114
+ }
115
+ }
116
+
117
+ &--destructive {
118
+ background-color: var(--color-negative-default);
119
+ border-color: var(--color-negative-default);
120
+ color: var(--content-invert-default);
121
+
122
+ &:hover {
123
+ background-color: var(--color-negative-hover);
124
+ border-color: var(--color-negative-hover);
125
+ }
126
+
127
+ &:active {
128
+ background-color: var(--color-negative-active);
129
+ border-color: var(--color-negative-active);
130
+ }
131
+
132
+ &[disabled] {
133
+ background-color: var(--color-negative-disabled);
134
+ border-color: var(--color-negative-disabled);
135
+ }
136
+ }
137
+
138
+ &--compact {
139
+ height: 32px;
140
+ min-width: 32px;
141
+ padding: 6px 16px;
142
+
143
+ &.#{$base-class}--icon-only {
144
+ padding: 4px;
145
+ }
146
+ }
147
+
148
+ &--large {
149
+ height: 42px;
150
+ min-width: 42px;
151
+ padding: 11px 16px;
152
+
153
+ &.#{$base-class}--icon-only {
154
+ padding: 9px;
155
+ }
156
+ }
157
+
158
+ &--text,
159
+ &--plain,
160
+ &--plain-light {
161
+ background-color: transparent;
162
+ border-color: transparent;
163
+ color: var(--color-action-default);
164
+
165
+ &:hover {
166
+ color: var(--color-action-hover);
167
+ }
168
+
169
+ &[disabled] {
170
+ color: var(--color-action-disabled);
171
+ }
172
+ }
173
+
174
+ &--plain-light {
175
+ font-weight: 400;
176
+ }
177
+
178
+ &--plain {
179
+ font-weight: 600;
180
+ }
181
+
182
+ &--plain,
183
+ &--plain-light {
184
+ border: 0;
185
+ height: auto;
186
+ min-width: auto;
187
+ padding: 0;
188
+
189
+ &:hover,
190
+ &:focus {
191
+ box-shadow: none;
192
+ text-decoration: underline;
193
+ }
194
+
195
+ &:disabled {
196
+ text-decoration: none;
197
+ }
198
+ }
199
+
200
+ &--loading {
201
+ cursor: progress;
202
+ transition: all 0.2s cubic-bezier(0.64, 0, 0.35, 1);
203
+
204
+ > * {
205
+ opacity: 0;
206
+ }
207
+
208
+ .#{$base-class}__loader {
209
+ align-items: center;
210
+ box-sizing: border-box;
211
+ display: flex;
212
+ height: 100%;
213
+ justify-content: center;
214
+ left: 0;
215
+ opacity: 1;
216
+ padding: inherit;
217
+ position: absolute;
218
+ top: 0;
219
+ width: 100%;
220
+
221
+ > * {
222
+ color: inherit;
223
+ font-weight: inherit;
224
+ margin: 0 5px;
225
+ overflow: hidden;
226
+ text-align: left;
227
+ text-overflow: ellipsis;
228
+ white-space: nowrap;
229
+ }
230
+ }
231
+ }
232
+
233
+ // TODO: Investigate
234
+ &__content {
235
+ margin-top: -2px;
236
+ }
237
+
238
+ &__icon {
239
+ color: inherit;
240
+ display: inline-block;
241
+ height: 20px;
242
+ line-height: 1;
243
+ width: 20px;
244
+
245
+ &--left {
246
+ margin-left: -4px;
247
+ margin-right: 5px;
248
+ order: -1;
249
+ }
250
+
251
+ &--right {
252
+ margin-left: 5px;
253
+ margin-right: -4px;
254
+ order: 1;
255
+ }
256
+ }
257
+ }
@@ -0,0 +1,110 @@
1
+ import * as React from 'react';
2
+
3
+ import { render } from 'test-utils';
4
+ import { Icon } from '../Icon';
5
+
6
+ import { Button } from './Button';
7
+ import styles from './Button.module.scss';
8
+
9
+ describe('<Button> component', () => {
10
+ function renderButton(props = {}) {
11
+ const result = render(<Button {...props} />);
12
+
13
+ return {
14
+ ...result,
15
+ btnEl: result.container.firstChild,
16
+ };
17
+ }
18
+
19
+ it('should have default set of classNames', () => {
20
+ const { btnEl } = renderButton();
21
+
22
+ const expectedClasses = ['btn', 'btn--basic', 'btn--medium'].map(
23
+ (cls) => styles[cls]
24
+ );
25
+ const notExpectedClasses = [
26
+ 'btn--loading',
27
+ 'btn--full-width',
28
+ 'btn--icon-only',
29
+ ].map((cls) => styles[cls]);
30
+
31
+ expect(btnEl).toHaveClass(...expectedClasses);
32
+ expect(btnEl).not.toHaveClass(...notExpectedClasses);
33
+ });
34
+
35
+ it('should allow to pass custom className', () => {
36
+ const { btnEl } = renderButton({ className: 'my-class' });
37
+
38
+ expect(btnEl).toHaveClass('my-class');
39
+ });
40
+
41
+ it('should not show loader by deafult', () => {
42
+ const { container, btnEl } = renderButton();
43
+
44
+ expect(btnEl).not.toHaveClass(styles['btn--loading']);
45
+ expect(
46
+ container.querySelector(`.${styles['btn__loader']}`)
47
+ ).not.toBeInTheDocument();
48
+ });
49
+
50
+ it('should show loader when "loading" prop is true', () => {
51
+ const { container, btnEl } = renderButton({ loading: true });
52
+
53
+ expect(btnEl).toHaveClass(styles['btn--loading']);
54
+ expect(container.querySelector(`.${styles['btn__loader']}`)).toBeVisible();
55
+ });
56
+
57
+ it('should not show icon by default', () => {
58
+ const { container } = renderButton();
59
+
60
+ expect(
61
+ container.querySelector(`.${styles['btn__icon']}`)
62
+ ).not.toBeInTheDocument();
63
+ });
64
+
65
+ it('should show icon component when "icon" prop has been passed', () => {
66
+ const { container } = renderButton({
67
+ icon: <Icon source="span" />,
68
+ });
69
+
70
+ const iconEl = container.querySelector(`.${styles['btn__icon']}`);
71
+
72
+ expect(iconEl).toBeVisible();
73
+ expect(iconEl).toHaveClass(styles['btn__icon--left']);
74
+ });
75
+
76
+ it('should show icon component on right side', () => {
77
+ const { container } = renderButton({
78
+ icon: <Icon source="span" />,
79
+ iconPosition: 'right',
80
+ });
81
+
82
+ const iconEl = container.querySelector(`.${styles['btn__icon']}`);
83
+
84
+ expect(iconEl).toBeVisible();
85
+ expect(iconEl).toHaveClass(styles['btn__icon--right']);
86
+ });
87
+
88
+ it('should allow to take "fullWidth"', () => {
89
+ const { btnEl } = renderButton({ fullWidth: true });
90
+
91
+ expect(btnEl).toHaveClass(styles['btn--full-width']);
92
+ });
93
+
94
+ it('should allow for "icon-only" content', () => {
95
+ const { btnEl } = renderButton({
96
+ children: null,
97
+ icon: <Icon source="span" />,
98
+ });
99
+
100
+ expect(btnEl).toHaveClass(styles['btn--icon-only']);
101
+ });
102
+
103
+ it('should render as an anchor element if href is passed', () => {
104
+ const { btnEl: linkEl } = renderButton({ href: 'https://example.com' });
105
+ const { btnEl } = renderButton({ href: '' });
106
+
107
+ expect((btnEl as HTMLElement).tagName).toBe('BUTTON');
108
+ expect((linkEl as HTMLElement).tagName).toBe('A');
109
+ });
110
+ });
@@ -0,0 +1,152 @@
1
+ import * as React from 'react';
2
+ import { ComponentMeta } from '@storybook/react';
3
+
4
+ import * as MaterialIcons from '@livechat/design-system-icons/react/material';
5
+
6
+ import { Icon } from '../Icon';
7
+ import { Button, ButtonProps } from './Button';
8
+
9
+ const icons = Object.fromEntries(
10
+ Object.entries(MaterialIcons).map(([key, source]) => [
11
+ key,
12
+ <Icon source={source as React.FC} />,
13
+ ])
14
+ );
15
+
16
+ const iterator = Object.keys(icons);
17
+
18
+ export default {
19
+ title: 'Components/Button',
20
+ component: Button,
21
+ argTypes: {
22
+ icon: {
23
+ options: ['None', ...iterator],
24
+ mapping: Object.assign(icons, { None: null }),
25
+ control: {
26
+ type: 'select',
27
+ labels: iterator,
28
+ },
29
+ },
30
+ },
31
+ parameters: {
32
+ controls: {
33
+ sort: 'alpha',
34
+ },
35
+ },
36
+ } as ComponentMeta<typeof Button>;
37
+
38
+ export const button = (args: ButtonProps): React.ReactElement => (
39
+ <Button {...args} />
40
+ );
41
+ button.args = {
42
+ loading: false,
43
+ disabled: false,
44
+ size: 'medium',
45
+ kind: 'primary',
46
+ children: 'Button Text',
47
+ fullWidth: false,
48
+ iconPosition: 'left',
49
+ icon: 'None',
50
+ };
51
+
52
+ export const kinds = (): React.ReactElement => (
53
+ <div>
54
+ <div className="spacer">
55
+ <Button>Basic</Button>
56
+ <Button kind="primary">Primary</Button>
57
+ <Button kind="secondary">Secondary</Button>
58
+ <Button kind="destructive">Destructive</Button>
59
+ <Button kind="text">Text</Button>
60
+ <Button kind="plain">Plain</Button>
61
+ <Button kind="plain-light">Plain light</Button>
62
+ </div>
63
+ </div>
64
+ );
65
+
66
+ export const states = (): React.ReactElement => (
67
+ <div>
68
+ <div className="spacer">
69
+ <Button disabled>Disabled</Button>
70
+ <Button disabled kind="primary">
71
+ Disabled
72
+ </Button>
73
+ <Button disabled kind="secondary">
74
+ Disabled
75
+ </Button>
76
+ <Button disabled kind="destructive">
77
+ Disabled
78
+ </Button>
79
+ <Button disabled kind="text">
80
+ Disabled
81
+ </Button>
82
+ <Button disabled kind="plain">
83
+ Disabled
84
+ </Button>
85
+ <Button disabled kind="plain-light">
86
+ Disabled
87
+ </Button>
88
+ </div>
89
+ <div className="spacer">
90
+ <Button loading>Loading</Button>
91
+ <Button loading kind="primary">
92
+ Loading
93
+ </Button>
94
+ <Button loading kind="secondary">
95
+ Loading
96
+ </Button>
97
+ <Button loading kind="destructive">
98
+ Loading
99
+ </Button>
100
+ <Button loading kind="text">
101
+ Loading
102
+ </Button>
103
+ <Button loading kind="plain">
104
+ Loading
105
+ </Button>
106
+ <Button loading kind="plain-light">
107
+ Loading
108
+ </Button>
109
+ </div>
110
+ </div>
111
+ );
112
+
113
+ export const sizes = (): React.ReactElement => (
114
+ <>
115
+ <div className="spacer">
116
+ <Button size="compact" kind="primary">
117
+ Compact
118
+ </Button>
119
+ <Button size="medium" kind="primary">
120
+ Medium (default)
121
+ </Button>
122
+ <Button size="large" kind="primary">
123
+ Large
124
+ </Button>
125
+ </div>
126
+ <div className="spacer">
127
+ <Button
128
+ icon={<Icon source={MaterialIcons.AddCircle} />}
129
+ size="compact"
130
+ kind="primary"
131
+ />
132
+ <Button
133
+ icon={<Icon source={MaterialIcons.AddCircle} />}
134
+ size="medium"
135
+ kind="primary"
136
+ />
137
+ <Button
138
+ icon={<Icon source={MaterialIcons.AddCircle} />}
139
+ size="large"
140
+ kind="primary"
141
+ />
142
+ </div>
143
+ </>
144
+ );
145
+
146
+ export const buttonAsLink = (args: ButtonProps): React.ReactElement => (
147
+ <Button href="https://livechat.com" target="_blank" kind="primary" {...args}>
148
+ Button as an external link to livechat.com
149
+ </Button>
150
+ );
151
+
152
+ buttonAsLink.args = { ...button.args, href: 'https://livechat.com' };
@@ -0,0 +1,88 @@
1
+ import * as React from 'react';
2
+ import cx from 'clsx';
3
+ import { Loader } from '../Loader';
4
+
5
+ import styles from './Button.module.scss';
6
+
7
+ export type ButtonSize = 'compact' | 'medium' | 'large';
8
+
9
+ export type ButtonProps = {
10
+ kind?:
11
+ | 'basic'
12
+ | 'primary'
13
+ | 'secondary'
14
+ | 'destructive'
15
+ | 'text'
16
+ | 'plain'
17
+ | 'plain-light';
18
+ size?: ButtonSize;
19
+ disabled?: boolean;
20
+ loading?: boolean;
21
+ fullWidth?: boolean;
22
+ loaderLabel?: string;
23
+ icon?: React.ReactElement;
24
+ iconPosition?: 'left' | 'right';
25
+ } & React.ButtonHTMLAttributes<HTMLButtonElement> &
26
+ React.AnchorHTMLAttributes<HTMLAnchorElement>;
27
+
28
+ const baseClass = 'btn';
29
+
30
+ export const Button: React.FC<ButtonProps> = ({
31
+ loading = false,
32
+ disabled = false,
33
+ type = 'button',
34
+ fullWidth = false,
35
+ kind = 'basic',
36
+ size = 'medium',
37
+ icon = null,
38
+ iconPosition = 'left',
39
+ loaderLabel,
40
+ className,
41
+ children,
42
+ href,
43
+ ...props
44
+ }) => {
45
+ const isDisabled = loading || disabled;
46
+
47
+ const Component = href ? 'a' : 'button';
48
+
49
+ const mergedClassNames = cx(
50
+ className,
51
+ styles[baseClass],
52
+ styles[`${baseClass}--${kind}`],
53
+ styles[`${baseClass}--${size}`],
54
+ {
55
+ [styles[`${baseClass}--loading`]]: loading,
56
+ [styles[`${baseClass}--full-width`]]: fullWidth,
57
+ [styles[`${baseClass}--icon-only`]]: !children && icon,
58
+ [styles[`${baseClass}--disabled`]]: isDisabled,
59
+ }
60
+ );
61
+
62
+ return (
63
+ <Component
64
+ className={mergedClassNames}
65
+ disabled={isDisabled}
66
+ type={type}
67
+ href={isDisabled ? undefined : href}
68
+ {...props}
69
+ >
70
+ {loading && (
71
+ <Loader
72
+ size="small"
73
+ label={loaderLabel}
74
+ className={styles[`${baseClass}__loader`]}
75
+ />
76
+ )}
77
+ {icon &&
78
+ React.cloneElement(icon, {
79
+ className: cx(
80
+ styles[`${baseClass}__icon`],
81
+ styles[`${baseClass}__icon--${iconPosition}`]
82
+ ),
83
+ disabled,
84
+ })}
85
+ <div className={styles[`${baseClass}__content`]}>{children}</div>
86
+ </Component>
87
+ );
88
+ };
@@ -0,0 +1,2 @@
1
+ export { Button } from './Button';
2
+ export type { ButtonSize, ButtonProps } from './Button';
@@ -0,0 +1,31 @@
1
+ .btn-group {
2
+ border: 1px solid var(--border-subtle);
3
+ border-radius: 4px;
4
+ display: inline-flex;
5
+ flex-direction: row;
6
+ padding: 3px;
7
+ vertical-align: middle;
8
+
9
+ .btn {
10
+ border-color: transparent;
11
+ color: var(--content-default);
12
+
13
+ &--active {
14
+ background-color: var(--surface-basic-active);
15
+ }
16
+
17
+ &--compact {
18
+ height: 24px;
19
+ }
20
+ &--medium {
21
+ height: 28px;
22
+ }
23
+ &--large {
24
+ height: 34px;
25
+ }
26
+ }
27
+
28
+ .btn + .btn {
29
+ margin-left: 2px;
30
+ }
31
+ }
@@ -0,0 +1,61 @@
1
+ import * as React from 'react';
2
+ import { render, userEvent, vi } from 'test-utils';
3
+ import noop from '../../utils/noop';
4
+ import { Button } from '../Button';
5
+ import { ButtonGroup } from './ButtonGroup';
6
+
7
+ import styles from './ButtonGroup.module.scss';
8
+
9
+ describe('<ButtonGroup> component', () => {
10
+ function renderComponent(props = {}, onButtonClick = noop) {
11
+ return render(
12
+ <ButtonGroup {...props}>
13
+ <Button onClick={onButtonClick}>First button</Button>
14
+ <Button>Second button</Button>
15
+ </ButtonGroup>
16
+ );
17
+ }
18
+
19
+ it('should have custom css class', () => {
20
+ const className = 'my-custom-class';
21
+ const {
22
+ container: { firstChild: el },
23
+ } = renderComponent({ className });
24
+
25
+ expect(el).toHaveClass(className);
26
+ });
27
+
28
+ it('should allow for controlled version of component by passing "currentIndex" prop', () => {
29
+ const { getAllByRole } = renderComponent({ currentIndex: 1 });
30
+
31
+ const [firstButton, secondButton] = getAllByRole('button');
32
+
33
+ expect(firstButton).not.toHaveClass(styles['btn--active']);
34
+ expect(secondButton).toHaveClass(styles['btn--active']);
35
+ });
36
+
37
+ it('should not have active button by default in unconrolled version', () => {
38
+ const { getAllByRole } = renderComponent();
39
+
40
+ const [firstButton, secondButton] = getAllByRole('button');
41
+
42
+ expect(firstButton).not.toHaveClass(styles['btn--active']);
43
+ expect(secondButton).not.toHaveClass(styles['btn--active']);
44
+ });
45
+
46
+ it('should call "onIndexChange" with index of current selected button on click', () => {
47
+ const onIndexChange = vi.fn();
48
+ const onButtonClick = vi.fn();
49
+ const { getAllByRole } = renderComponent({ onIndexChange }, onButtonClick);
50
+
51
+ const [firstButton] = getAllByRole('button');
52
+
53
+ expect(firstButton).not.toHaveClass(styles['btn--active']);
54
+
55
+ userEvent.click(firstButton);
56
+
57
+ expect(onIndexChange).toHaveBeenCalledWith(0, expect.anything());
58
+ expect(onButtonClick).toHaveBeenCalled();
59
+ expect(firstButton).toHaveClass(styles['btn--active']);
60
+ });
61
+ });